1 /*****************************************************************************\
4 * Author : Chris Koeritz *
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 \*****************************************************************************/
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>
29 using namespace basis;
30 using namespace loggers;
31 using namespace structures;
32 using namespace textual;
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...
43 amorph<letter> _waiting;
45 mail_cabinet() : _waiting(0) {}
47 ~mail_cabinet() { _waiting.reset(); }
49 mail_cabinet(mail_cabinet &formal(to_copy)) {
50 non_continuable_error("mail_cabinet", "copy constructor", "should never be called");
53 mail_cabinet &operator =(mail_cabinet &formal(to_copy)) {
54 non_continuable_error("mail_cabinet", "assignment operator",
55 "should never be called");
62 class mailbox_bank : public int_hash<mail_cabinet>
65 mailbox_bank() : int_hash<mail_cabinet> (MAILBOX_BITS) {}
66 ~mailbox_bank() { reset(); }
68 void get_ids(int_set &to_fill);
69 // returns the list of identifiers for people with mailboxes.
71 void add_cabinet(const unique_int &id);
72 // creates a new mail receptacle for the "id".
74 bool zap_cabinet(const unique_int &id);
75 // removes the cabinet for "id".
77 void add_item(const unique_int &id, letter *to_add);
78 // stuffs an item "to_add" in for "id".
80 bool get(const unique_int &id, letter * &to_receive);
81 // retrieves the next waiting package for "id" into "to_receive".
84 // gets rid of any cabinets without any packages.
87 void mailbox_bank::clean_up()
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]);
98 void mailbox_bank::get_ids(int_set &to_fill) { to_fill = ids(); }
100 void mailbox_bank::add_cabinet(const unique_int &id)
102 if (find(id.raw_id())) return; // already exists.
103 mail_cabinet *to_add = new mail_cabinet;
104 add(id.raw_id(), to_add);
107 bool mailbox_bank::zap_cabinet(const unique_int &id)
109 if (!find(id.raw_id())) return false; // doesn't exist.
110 return zap(id.raw_id());
113 void mailbox_bank::add_item(const unique_int &id, letter *to_add)
115 mail_cabinet *found = find(id.raw_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).
126 found->_waiting.append(to_add);
129 bool mailbox_bank::get(const unique_int &id, letter * &to_receive)
131 mail_cabinet *found = find(id.raw_id());
132 if (!found) return false; // no cabinet, much less any mail.
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);
149 : _transaction_lock(new mutex),
150 _packages(new mailbox_bank)
157 WHACK(_transaction_lock);
160 void mailbox::get_ids(int_set &to_fill)
162 auto_synchronizer l(*_transaction_lock);
163 _packages->get_ids(to_fill);
166 void mailbox::drop_off(const unique_int &id, letter *package)
168 auto_synchronizer l(*_transaction_lock);
169 _packages->add_item(id, package);
172 void mailbox::clean_up()
174 auto_synchronizer l(*_transaction_lock);
175 _packages->clean_up();
178 int mailbox::waiting(const unique_int &id) const
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.
185 to_return = found->_waiting.elements();
189 bool mailbox::pick_up(const unique_int &id, letter * &package)
191 package = NULL_POINTER;
192 auto_synchronizer l(*_transaction_lock);
193 return _packages->get(id, package);
196 bool mailbox::close_out(const unique_int &id)
198 auto_synchronizer l(*_transaction_lock);
199 bool ret = _packages->zap_cabinet(id);
203 void mailbox::show(astring &to_fill)
205 auto_synchronizer l(*_transaction_lock);
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);
216 to_fill += string_manipulation::indentation(4)
217 + astring(astring::SPRINTF, "%4ld: ", j + 1)
218 + text + parser_bits::platform_eol_to_chars();
223 void mailbox::limit_boxes(int max_letters)
225 auto_synchronizer l(*_transaction_lock);
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);
237 void mailbox::apply(apply_function *to_apply, void *data_link)
239 auto_synchronizer l(*_transaction_lock);
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.