wow. that was easy: git mv core nucleus
[feisty_meow.git] / nucleus / library / processes / mailbox.cpp
diff --git a/nucleus/library/processes/mailbox.cpp b/nucleus/library/processes/mailbox.cpp
new file mode 100644 (file)
index 0000000..52b89ad
--- /dev/null
@@ -0,0 +1,262 @@
+/*****************************************************************************\
+*                                                                             *
+*  Name   : mailbox                                                           *
+*  Author : Chris Koeritz                                                     *
+*                                                                             *
+*******************************************************************************
+* Copyright (c) 1998-$now By Author.  This program is free software; you can  *
+* redistribute it and/or modify it under the terms of the GNU General Public  *
+* License as published by the Free Software Foundation; either version 2 of   *
+* the License or (at your option) any later version.  This is online at:      *
+*     http://www.fsf.org/copyleft/gpl.html                                    *
+* Please send any updates to: fred@gruntose.com                               *
+\*****************************************************************************/
+
+#include "letter.h"
+#include "mailbox.h"
+
+#include <basis/astring.h>
+#include <basis/functions.h>
+#include <basis/guards.h>
+#include <basis/mutex.h>
+#include <loggers/critical_events.h>
+#include <structures/amorph.h>
+#include <structures/int_hash.h>
+#include <structures/unique_id.h>
+#include <textual/parser_bits.h>
+#include <textual/string_manipulation.h>
+
+using namespace basis;
+using namespace loggers;
+using namespace structures;
+using namespace textual;
+
+namespace processes {
+
+const int MAILBOX_BITS = 9;
+  // we allow N bits in our table size, which means the table will have 2^N
+  // elements.  careful with that increase...
+
+class mail_cabinet
+{
+public:
+  amorph<letter> _waiting;
+
+  mail_cabinet() : _waiting(0) {}
+
+  ~mail_cabinet() { _waiting.reset(); }
+
+  mail_cabinet(mail_cabinet &formal(to_copy)) {
+    non_continuable_error("mail_cabinet", "copy constructor", "should never be called");
+  }
+
+  mail_cabinet &operator =(mail_cabinet &formal(to_copy)) {
+    non_continuable_error("mail_cabinet", "assignment operator",
+        "should never be called");
+    return *this;
+  }
+};
+
+//////////////
+
+class mailbox_bank : public int_hash<mail_cabinet>
+{
+public:
+  mailbox_bank() : int_hash<mail_cabinet> (MAILBOX_BITS) {}
+  ~mailbox_bank() { reset(); }
+
+  void get_ids(int_set &to_fill);
+    // returns the list of identifiers for people with mailboxes.
+
+  void add_cabinet(const unique_int &id);
+    // creates a new mail receptacle for the "id".
+
+  bool zap_cabinet(const unique_int &id);
+    // removes the cabinet for "id".
+
+  void add_item(const unique_int &id, letter *to_add);
+    // stuffs an item "to_add" in for "id".
+
+  bool get(const unique_int &id, letter * &to_receive);
+    // retrieves the next waiting package for "id" into "to_receive".
+
+  void clean_up();
+    // gets rid of any cabinets without any packages.
+};
+
+void mailbox_bank::clean_up()
+{
+  int_set ids;
+  get_ids(ids);
+  for (int i = 0; i < ids.elements(); i++) {
+    mail_cabinet *entry = find(ids[i]);
+    // if the cabinet has zero elements, we zap it.
+    if (!entry->_waiting.elements()) zap(ids[i]);
+  }
+}
+
+void mailbox_bank::get_ids(int_set &to_fill) { to_fill = ids(); }
+
+void mailbox_bank::add_cabinet(const unique_int &id)
+{
+  if (find(id.raw_id())) return;  // already exists.
+  mail_cabinet *to_add = new mail_cabinet;
+  add(id.raw_id(), to_add);
+}
+
+bool mailbox_bank::zap_cabinet(const unique_int &id)
+{
+  if (!find(id.raw_id())) return false;  // doesn't exist.
+  return zap(id.raw_id());
+}
+
+void mailbox_bank::add_item(const unique_int &id, letter *to_add)
+{
+  mail_cabinet *found = find(id.raw_id());
+  if (!found) {
+    add_cabinet(id);
+    found = find(id.raw_id());
+    // there should never be a failure case that would prevent the new cabinet
+    // from being added (besides overall memory failure).
+    if (!found) {
+//complain
+      return;
+    }
+  }
+  found->_waiting.append(to_add);
+}
+
+bool mailbox_bank::get(const unique_int &id, letter * &to_receive)
+{
+  mail_cabinet *found = find(id.raw_id());
+  if (!found) return false;  // no cabinet, much less any mail.
+
+  if (!found->_waiting.elements()) return false;  // no mail waiting.
+  for (int i = 0; i < found->_waiting.elements(); i++) {
+    // check if its time is ripe...
+    if (!found->_waiting.borrow(i)->ready_to_send()) continue;
+    // get the waiting mail and remove its old slot.
+    to_receive = found->_waiting.acquire(i);
+    found->_waiting.zap(i, i);
+    return true;
+  }
+  return false;
+}
+
+//////////////
+
+mailbox::mailbox()
+: _transaction_lock(new mutex),
+  _packages(new mailbox_bank)
+{
+}
+
+mailbox::~mailbox()
+{
+  WHACK(_packages);
+  WHACK(_transaction_lock);
+}
+
+void mailbox::get_ids(int_set &to_fill)
+{
+  auto_synchronizer l(*_transaction_lock);
+  _packages->get_ids(to_fill);
+}
+
+void mailbox::drop_off(const unique_int &id, letter *package)
+{
+  auto_synchronizer l(*_transaction_lock);
+  _packages->add_item(id, package);
+}
+
+void mailbox::clean_up()
+{
+  auto_synchronizer l(*_transaction_lock);
+  _packages->clean_up();
+}
+
+int mailbox::waiting(const unique_int &id) const
+{
+  auto_synchronizer l(*_transaction_lock);
+  mail_cabinet *found = _packages->find(id.raw_id());
+  int to_return = 0;  // if no cabinet, this is the proper count.
+  // if there is a cabinet, then get the size.
+  if (found)
+    to_return = found->_waiting.elements();
+  return to_return;
+}
+
+bool mailbox::pick_up(const unique_int &id, letter * &package)
+{
+  package = NIL;
+  auto_synchronizer l(*_transaction_lock);
+  return _packages->get(id, package);
+}
+
+bool mailbox::close_out(const unique_int &id)
+{
+  auto_synchronizer l(*_transaction_lock);
+  bool ret = _packages->zap_cabinet(id);
+  return ret;
+}
+
+void mailbox::show(astring &to_fill)
+{
+  auto_synchronizer l(*_transaction_lock);
+  int_set ids;
+  _packages->get_ids(ids);
+  for (int i = 0; i < ids.elements(); i++) {
+    mail_cabinet &mc = *_packages->find(ids[i]);
+    to_fill += astring(astring::SPRINTF, "cabinet %d:", ids[i])
+        + parser_bits::platform_eol_to_chars();
+    for (int j = 0; j < mc._waiting.elements(); j++) {
+      letter &l = *mc._waiting.borrow(j);
+      astring text;
+      l.text_form(text);
+      to_fill += string_manipulation::indentation(4)
+          + astring(astring::SPRINTF, "%4ld: ", j + 1)
+          + text + parser_bits::platform_eol_to_chars();
+    }
+  }
+}
+
+void mailbox::limit_boxes(int max_letters)
+{
+  auto_synchronizer l(*_transaction_lock);
+  int_set ids;
+  _packages->get_ids(ids);
+  for (int i = 0; i < ids.elements(); i++) {
+    mail_cabinet &mc = *_packages->find(ids[i]);
+    if (mc._waiting.elements() > max_letters) {
+      // this one needs cleaning.
+      mc._waiting.zap(max_letters, mc._waiting.elements() - 1);
+    }
+  }
+}
+
+void mailbox::apply(apply_function *to_apply, void *data_link)
+{
+  auto_synchronizer l(*_transaction_lock);
+  int_set ids;
+  _packages->get_ids(ids);
+  for (int i = 0; i < ids.elements(); i++) {
+    mail_cabinet &mc = *_packages->find(ids[i]);
+    for (int j = 0; j < mc._waiting.elements(); j++) {
+      letter &l = *mc._waiting.borrow(j);
+      outcome ret = to_apply(l, ids[i], data_link);
+      if ( (ret == APPLY_WHACK) || (ret == APPLY_WHACK_STOP) ) {
+        // they wanted this node removed.
+        mc._waiting.zap(j, j);
+        j--;  // skip back before missing guy so we don't omit anyone.
+        if (ret == APPLY_WHACK_STOP)
+          break;  // they wanted to be done with it also.
+      } else if (ret == APPLY_STOP) {
+        break;  // we hit the exit condition.
+      }
+    }
+  }
+}
+
+} //namespace.
+
+