first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / library / processes / mailbox.cpp
1 /*****************************************************************************\
2 *                                                                             *
3 *  Name   : mailbox                                                           *
4 *  Author : Chris Koeritz                                                     *
5 *                                                                             *
6 *******************************************************************************
7 * Copyright (c) 1998-$now By Author.  This program is free software; you can  *
8 * redistribute it and/or modify it under the terms of the GNU General Public  *
9 * License as published by the Free Software Foundation; either version 2 of   *
10 * the License or (at your option) any later version.  This is online at:      *
11 *     http://www.fsf.org/copyleft/gpl.html                                    *
12 * Please send any updates to: fred@gruntose.com                               *
13 \*****************************************************************************/
14
15 #include "letter.h"
16 #include "mailbox.h"
17
18 #include <basis/astring.h>
19 #include <basis/functions.h>
20 #include <basis/guards.h>
21 #include <basis/mutex.h>
22 #include <loggers/critical_events.h>
23 #include <structures/amorph.h>
24 #include <structures/int_hash.h>
25 #include <structures/unique_id.h>
26 #include <textual/parser_bits.h>
27 #include <textual/string_manipulation.h>
28
29 using namespace basis;
30 using namespace loggers;
31 using namespace structures;
32 using namespace textual;
33
34 namespace processes {
35
36 const int MAILBOX_BITS = 9;
37   // we allow N bits in our table size, which means the table will have 2^N
38   // elements.  careful with that increase...
39
40 class mail_cabinet
41 {
42 public:
43   amorph<letter> _waiting;
44
45   mail_cabinet() : _waiting(0) {}
46
47   ~mail_cabinet() { _waiting.reset(); }
48
49   mail_cabinet(mail_cabinet &formal(to_copy)) {
50     non_continuable_error("mail_cabinet", "copy constructor", "should never be called");
51   }
52
53   mail_cabinet &operator =(mail_cabinet &formal(to_copy)) {
54     non_continuable_error("mail_cabinet", "assignment operator",
55         "should never be called");
56     return *this;
57   }
58 };
59
60 //////////////
61
62 class mailbox_bank : public int_hash<mail_cabinet>
63 {
64 public:
65   mailbox_bank() : int_hash<mail_cabinet> (MAILBOX_BITS) {}
66   ~mailbox_bank() { reset(); }
67
68   void get_ids(int_set &to_fill);
69     // returns the list of identifiers for people with mailboxes.
70
71   void add_cabinet(const unique_int &id);
72     // creates a new mail receptacle for the "id".
73
74   bool zap_cabinet(const unique_int &id);
75     // removes the cabinet for "id".
76
77   void add_item(const unique_int &id, letter *to_add);
78     // stuffs an item "to_add" in for "id".
79
80   bool get(const unique_int &id, letter * &to_receive);
81     // retrieves the next waiting package for "id" into "to_receive".
82
83   void clean_up();
84     // gets rid of any cabinets without any packages.
85 };
86
87 void mailbox_bank::clean_up()
88 {
89   int_set ids;
90   get_ids(ids);
91   for (int i = 0; i < ids.elements(); i++) {
92     mail_cabinet *entry = find(ids[i]);
93     // if the cabinet has zero elements, we zap it.
94     if (!entry->_waiting.elements()) zap(ids[i]);
95   }
96 }
97
98 void mailbox_bank::get_ids(int_set &to_fill) { to_fill = ids(); }
99
100 void mailbox_bank::add_cabinet(const unique_int &id)
101 {
102   if (find(id.raw_id())) return;  // already exists.
103   mail_cabinet *to_add = new mail_cabinet;
104   add(id.raw_id(), to_add);
105 }
106
107 bool mailbox_bank::zap_cabinet(const unique_int &id)
108 {
109   if (!find(id.raw_id())) return false;  // doesn't exist.
110   return zap(id.raw_id());
111 }
112
113 void mailbox_bank::add_item(const unique_int &id, letter *to_add)
114 {
115   mail_cabinet *found = find(id.raw_id());
116   if (!found) {
117     add_cabinet(id);
118     found = find(id.raw_id());
119     // there should never be a failure case that would prevent the new cabinet
120     // from being added (besides overall memory failure).
121     if (!found) {
122 //complain
123       return;
124     }
125   }
126   found->_waiting.append(to_add);
127 }
128
129 bool mailbox_bank::get(const unique_int &id, letter * &to_receive)
130 {
131   mail_cabinet *found = find(id.raw_id());
132   if (!found) return false;  // no cabinet, much less any mail.
133
134   if (!found->_waiting.elements()) return false;  // no mail waiting.
135   for (int i = 0; i < found->_waiting.elements(); i++) {
136     // check if its time is ripe...
137     if (!found->_waiting.borrow(i)->ready_to_send()) continue;
138     // get the waiting mail and remove its old slot.
139     to_receive = found->_waiting.acquire(i);
140     found->_waiting.zap(i, i);
141     return true;
142   }
143   return false;
144 }
145
146 //////////////
147
148 mailbox::mailbox()
149 : _transaction_lock(new mutex),
150   _packages(new mailbox_bank)
151 {
152 }
153
154 mailbox::~mailbox()
155 {
156   WHACK(_packages);
157   WHACK(_transaction_lock);
158 }
159
160 void mailbox::get_ids(int_set &to_fill)
161 {
162   auto_synchronizer l(*_transaction_lock);
163   _packages->get_ids(to_fill);
164 }
165
166 void mailbox::drop_off(const unique_int &id, letter *package)
167 {
168   auto_synchronizer l(*_transaction_lock);
169   _packages->add_item(id, package);
170 }
171
172 void mailbox::clean_up()
173 {
174   auto_synchronizer l(*_transaction_lock);
175   _packages->clean_up();
176 }
177
178 int mailbox::waiting(const unique_int &id) const
179 {
180   auto_synchronizer l(*_transaction_lock);
181   mail_cabinet *found = _packages->find(id.raw_id());
182   int to_return = 0;  // if no cabinet, this is the proper count.
183   // if there is a cabinet, then get the size.
184   if (found)
185     to_return = found->_waiting.elements();
186   return to_return;
187 }
188
189 bool mailbox::pick_up(const unique_int &id, letter * &package)
190 {
191   package = NIL;
192   auto_synchronizer l(*_transaction_lock);
193   return _packages->get(id, package);
194 }
195
196 bool mailbox::close_out(const unique_int &id)
197 {
198   auto_synchronizer l(*_transaction_lock);
199   bool ret = _packages->zap_cabinet(id);
200   return ret;
201 }
202
203 void mailbox::show(astring &to_fill)
204 {
205   auto_synchronizer l(*_transaction_lock);
206   int_set ids;
207   _packages->get_ids(ids);
208   for (int i = 0; i < ids.elements(); i++) {
209     mail_cabinet &mc = *_packages->find(ids[i]);
210     to_fill += astring(astring::SPRINTF, "cabinet %d:", ids[i])
211         + parser_bits::platform_eol_to_chars();
212     for (int j = 0; j < mc._waiting.elements(); j++) {
213       letter &l = *mc._waiting.borrow(j);
214       astring text;
215       l.text_form(text);
216       to_fill += string_manipulation::indentation(4)
217           + astring(astring::SPRINTF, "%4ld: ", j + 1)
218           + text + parser_bits::platform_eol_to_chars();
219     }
220   }
221 }
222
223 void mailbox::limit_boxes(int max_letters)
224 {
225   auto_synchronizer l(*_transaction_lock);
226   int_set ids;
227   _packages->get_ids(ids);
228   for (int i = 0; i < ids.elements(); i++) {
229     mail_cabinet &mc = *_packages->find(ids[i]);
230     if (mc._waiting.elements() > max_letters) {
231       // this one needs cleaning.
232       mc._waiting.zap(max_letters, mc._waiting.elements() - 1);
233     }
234   }
235 }
236
237 void mailbox::apply(apply_function *to_apply, void *data_link)
238 {
239   auto_synchronizer l(*_transaction_lock);
240   int_set ids;
241   _packages->get_ids(ids);
242   for (int i = 0; i < ids.elements(); i++) {
243     mail_cabinet &mc = *_packages->find(ids[i]);
244     for (int j = 0; j < mc._waiting.elements(); j++) {
245       letter &l = *mc._waiting.borrow(j);
246       outcome ret = to_apply(l, ids[i], data_link);
247       if ( (ret == APPLY_WHACK) || (ret == APPLY_WHACK_STOP) ) {
248         // they wanted this node removed.
249         mc._waiting.zap(j, j);
250         j--;  // skip back before missing guy so we don't omit anyone.
251         if (ret == APPLY_WHACK_STOP)
252           break;  // they wanted to be done with it also.
253       } else if (ret == APPLY_STOP) {
254         break;  // we hit the exit condition.
255       }
256     }
257   }
258 }
259
260 } //namespace.
261
262