Branch data Line data Source code
1 : : /* Copyright 2006 Savarese Software Research Corporation
2 : : *
3 : : * Licensed under the Apache License, Version 2.0 (the "License");
4 : : * you may not use this file except in compliance with the License.
5 : : * You may obtain a copy of the License at
6 : : *
7 : : * http://www.savarese.com/software/ApacheLicense-2.0
8 : : *
9 : : * Unless required by applicable law or agreed to in writing, software
10 : : * distributed under the License is distributed on an "AS IS" BASIS,
11 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : : * See the License for the specific language governing permissions and
13 : : * limitations under the License.
14 : : */
15 : :
16 : : /**
17 : : * @file
18 : : * This header defines the Mailbox class.
19 : : */
20 : :
21 : : #ifndef __SSRC_SPREAD_MAILBOX_H
22 : : #define __SSRC_SPREAD_MAILBOX_H
23 : :
24 : : #include <ssrc/spread/ScatterMessage.h>
25 : :
26 : : __BEGIN_NS_SSRC_SPREAD
27 : :
28 : : /**
29 : : * Timeout is a simple wrapper around Spread::sp_time the %Spread C
30 : : * API's Spread::sp_time struct that facilitates specifying connection
31 : : * timeouts for the Mailbox constructor. The constructor will convert
32 : : * an integral number into a Timeout instance, allowing you to specify
33 : : * timeouts to the Mailbox constructor as either Timeout instances or
34 : : * a single number (interpreted as a number of seconds).
35 : : */
36 : : class Timeout {
37 : : Spread::sp_time time;
38 : :
39 : : public:
40 : : /**
41 : : * Converts a Spread::sp_time instance to a Timeout instance, copying
42 : : * the stored time representation in the process.
43 : : *
44 : : * @param time The Spread::sp_time instance to convert.
45 : : */
46 : : Timeout(const Spread::sp_time time) : time(time) { }
47 : :
48 : : /**
49 : : * Creates a Timeout instance representing a number of seconds
50 : : * plus microseconds. The constructor also is able to convert
51 : : * an int or long to a Timeout, interpreting the number as the
52 : : * first constructor argument (the number of seconds).
53 : : *
54 : : * @param sec The number of seconds.
55 : : * @param usec The number of microseconds.
56 : : */
57 : 1 : Timeout(const long sec = 0, const long usec = 0) {
58 : 1 : time.sec = sec, time.usec = usec;
59 : 1 : }
60 : :
61 : : /**
62 : : * Converts a Timeout instance to a Spread::sp_time instance.
63 : : *
64 : : * @return A Spread::sp_time value representing the same time value.
65 : : */
66 : 20 : operator Spread::sp_time() const { return time; }
67 : : };
68 : :
69 : : /**
70 : : * The Mailbox class wraps the file descriptor returned after
71 : : * establishing a connection to a %Spread daemon and the operations
72 : : * that can be performed with it. The class is not named Connection
73 : : * because it can lead to confusion. The connection is between the
74 : : * client application and the %Spread daemon, but through that
75 : : * connection the client application can send and receive messages to
76 : : * and from multiple destinations. We feel that Mailbox is the more
77 : : * conceptually appropriate metaphor. Messages can be sent and
78 : : * received to and from multiple destinations through a mailbox. The
79 : : * %Spread daemon acts as a post office, mediating and routing the
80 : : * message transmission and retrieval. A connection is a
81 : : * point-to-point concept. Beyond initializing a Mailbox, the client
82 : : * application need not be conscious that there is a %Spread daemon in
83 : : * the communication path.
84 : : *
85 : : * A Mailbox provides three different modes of interaction for sending
86 : : * and receivng messages. The first mode requires you to provide the
87 : : * message and destination/source group list on every send and
88 : : * receive. The second mode involves using only the internal
89 : : * ScatterMessage and GroupList maintained by Mailbox. That is, you
90 : : * add groups and messages to Mailbox instead of your own GroupList
91 : : * and ScatterMessage. The third mode involves a mixture of the two,
92 : : * where you can specify either your own message or group list, and
93 : : * let the Mailbox internal message or group list provide the other.
94 : : *
95 : : * The second and third modes are intended as convenience methods for
96 : : * those programmers who prefer that model. The first mode is a
97 : : * direct analog to the %Spread C API and is what was originally
98 : : * intended as the primary mode of use. However, it turns out that
99 : : * the %Spread C API delegates all of the non-scatter send and receive
100 : : * functions to the scatter send and receive functions. Therefore,
101 : : * Mailbox uses only the scatter versions of the functions to do its
102 : : * work, bypassing a level of indirection. However, to support
103 : : * sending a single message to a single group, as in send(const
104 : : * Message &, const string &), Mailbox must maintain its own GroupList
105 : : * and place the group name in the GroupList. Also, it must maintain
106 : : * its own ScatterMessage and add the Message to the ScatterMessage
107 : : * before the send. In fact, this is what the non-scatter %Spread C
108 : : * API functions do. Since these scratch variables are maintained
109 : : * anyway to support the non-scatter convenience methods, we don't
110 : : * lose anything by making them availble in the public API. It is up
111 : : * to the programmer to choose between the methods. To disambiguate,
112 : : * whenever you provide your own ScatterMessage or GroupList, it is
113 : : * used directly, bypassing the internal scratch variables. The
114 : : * scratch variables are used only when you provide single-group
115 : : * string destination or non-scatter messages.
116 : : *
117 : : * @section mailbox_examples Examples
118 : : *
119 : : * @subsection mailbox_examples_mode1 Mode 1 (provide your own Message and GroupList)
120 : : * <pre>
121 : : * Mailbox mbox(...);
122 : : * ScatterMessage message;
123 : : * GroupList destination;
124 : : * char *data = "foo";
125 : : *
126 : : * destination.add("group1");
127 : : * destination.add("group2");
128 : : * message.add(data, 3);
129 : : * mbox.send(message, destination);
130 : : * </pre>
131 : : *
132 : : * @subsection mailbox_examples_mode2 Mode 2 (use Mailbox buffers)
133 : : * <pre>
134 : : * Mailbox mbox(...);
135 : : * char *data = "foo";
136 : : *
137 : : * mbox.clear_groups();
138 : : * mbox.add_group("group1");
139 : : * mbox.add_group("group2");
140 : : * mbox.clear_message_parts();
141 : : * mbox.add_message_part(data, 3);
142 : : * mbox.send();
143 : : * </pre>
144 : : *
145 : : * @subsection mailbox_examples_mode3 Mode 3 (mixed mode)
146 : : * <pre>
147 : : * Mailbox mbox(...);
148 : : * ScatterMessage message;
149 : : * char *data = "foo";
150 : : *
151 : : * mbox.clear_groups();
152 : : * mbox.add_group("group1");
153 : : * mbox.add_group("group2");
154 : : * message.add(data, 3);
155 : : * mbox.send(message);
156 : : * </pre>
157 : : */
158 : : class Mailbox {
159 : : public:
160 : : /** Defines the type for the Mailbox file descriptor. */
161 : : typedef Spread::mailbox descriptor_type;
162 : :
163 : : static const Timeout ZeroTimeout;
164 : :
165 : : /**
166 : : * The Priority enumeration defines the priority levels for establishing
167 : : * a mailbox connection.
168 : : */
169 : : enum Priority {
170 : : Low = LOW_PRIORITY, Medium = MEDIUM_PRIORITY, High = HIGH_PRIORITY
171 : : };
172 : :
173 : : private:
174 : : bool _group_membership, _drop_receive, _killed;
175 : : string _connection, _name, _private_group;
176 : : descriptor_type _mbox;
177 : : ScatterMessage _scatter;
178 : : GroupList _groups;
179 : :
180 : : public:
181 : :
182 : : explicit Mailbox(const string & connection = "", const string & name = "",
183 : : const bool group_membership = true,
184 : : const Timeout & timeout = ZeroTimeout,
185 : : const Priority priority = Medium) SSRC_DECL_THROW(Error);
186 : :
187 : : /**
188 : : * Disconnects from the %Spread daemon unless kill() was called during
189 : : * the lifetime of the object.
190 : : */
191 : 40 : ~Mailbox() {
192 [ + - ]: 20 : if(!killed())
193 : 20 : Spread::SP_disconnect(_mbox);
194 : 20 : }
195 : :
196 : : /**
197 : : * Returns the name of the %Spread daemon connection.
198 : : * @return The name of the %Spread daemon connection.
199 : : */
200 : : const string & connection() const {
201 : : return _connection;
202 : : }
203 : :
204 : : /**
205 : : * Returns the name of the Mailbox.
206 : : * @return The name of the Mailbox.
207 : : */
208 : : const string & name() const {
209 : : return _name;
210 : : }
211 : :
212 : : /**
213 : : * Returns the file descriptor of the connection to the %Spread daemon.
214 : : * This file descriptor is made available to allow you to hook into an
215 : : * event handling system (e.g., select, poll, Linux epoll).
216 : : * @return The file descriptor of the connection to the %Spread daemon.
217 : : */
218 : : descriptor_type descriptor() const {
219 : : return _mbox;
220 : : }
221 : :
222 : : /**
223 : : * Returns the private group name of the Mailbox.
224 : : * @return The private group name of the Mailbox.
225 : : */
226 : 13 : const string & private_group() const {
227 : 13 : return _private_group;
228 : : }
229 : :
230 : : /**
231 : : * Returns true if reception of group membership messages is
232 : : * enabled, false if not.
233 : : * @return true if reception of group membership messages is
234 : : * enabled, false if not.
235 : : */
236 : : bool group_membership() const {
237 : : return _group_membership;
238 : : }
239 : :
240 : : /**
241 : : * Sets whether or not received messages that are too large to fit
242 : : * in the provided buffer should be truncated. By default, Mailbox
243 : : * does not drop data and instead resizes Message instances to hold
244 : : * the data and retries the receive.
245 : : * @param drop true to drop excess data, false to preserve it and retry.
246 : : */
247 : : void set_drop_receive(const bool drop = true) {
248 : : _drop_receive = drop;
249 : : }
250 : :
251 : : /**
252 : : * Returns true if excess received data will be dropped, false if not.
253 : : * @return true if excess received data will be dropped, false if not.
254 : : */
255 : : bool drop_receive() const {
256 : : return _drop_receive;
257 : : }
258 : :
259 : : /**
260 : : * Joins the specified group.
261 : : *
262 : : * @param group The name of the group to join.
263 : : * @throw Error If the operation fails.
264 : : */
265 : 14 : void join(const string & group) SSRC_DECL_THROW(Error) {
266 : 14 : int result = Spread::SP_join(_mbox, group.c_str());
267 [ - + ]: 14 : if(result != 0)
268 : 0 : throw Error(result);
269 : 14 : }
270 : :
271 : : /**
272 : : * Leaves the specified group.
273 : : *
274 : : * @param group The name of the group to leave.
275 : : * @throw Error If the operation fails.
276 : : */
277 : 14 : void leave(const string & group) SSRC_DECL_THROW(Error) {
278 : 14 : int result = Spread::SP_leave(_mbox, group.c_str());
279 [ - + ]: 14 : if(result != 0)
280 : 0 : throw Error(result);
281 : 14 : }
282 : :
283 : : #ifdef LIBSSRCSPREAD_ENABLE_MAILBOX_KILL
284 : : /**
285 : : * Closes the connection to the %Spread daemon without notifying the
286 : : * daemon. If you call this, you can't use the object anymore! It
287 : : * is provided for forking purposes only, so that a child or parent
288 : : * may continue using the Mailbox while the other discontinues using
289 : : * it.
290 : : *
291 : : * <b>Warning:</b> This method is available only when compiled against
292 : : * %Spread 4.x and greater.
293 : : */
294 : : void kill() {
295 : : Spread::SP_kill(_mbox);
296 : : _killed = true;
297 : : }
298 : : #endif
299 : :
300 : : /**
301 : : * Returns true if kill() has been called, false if not.
302 : : *
303 : : * <b>Warning:</b> This method always returns false when not
304 : : * compiled against %Spread 4.x and greater.
305 : : *
306 : : * @return true if kill() has been called, false if not.
307 : : */
308 : 20 : bool killed() const {
309 : 20 : return _killed;
310 : : }
311 : :
312 : : /**
313 : : * Polls the Mailbox to see if any messags have arrived that can be
314 : : * retrieved via receive().
315 : : *
316 : : * @return The number of bytes available to be received (0 if there
317 : : * are no messages).
318 : : * @throw Error If the operation fails.
319 : : */
320 : : unsigned int poll() const SSRC_DECL_THROW(Error) {
321 : : int result = Spread::SP_poll(_mbox);
322 : : if(result < 0)
323 : : throw Error(result);
324 : : return result;
325 : : }
326 : :
327 : : /**
328 : : * Adds a message part to the internal ScatterMessage.
329 : : *
330 : : * @param data A pointer to the data buffer.
331 : : * @param size The size of the data buffer in bytes.
332 : : */
333 : 5 : bool add_message_part(const void *data, const unsigned int size) {
334 : 5 : return _scatter.add(data, size);
335 : : }
336 : :
337 : : /**
338 : : * Adds a Message to the internal ScatterMessage. The service
339 : : * type and message type of the Message will not be used in a send
340 : : * because the internal ScatterMessage may contain multiple Message
341 : : * parts. You must specify the types as parameters to the appropriate
342 : : * send() call.
343 : : *
344 : : * @param message The Message to add.
345 : : */
346 : 16 : bool add_message_part(const Message & message) {
347 : 16 : return _scatter.add(message);
348 : : }
349 : :
350 : : /**
351 : : * Appends a group name to the end of the internal GroupList.
352 : : *
353 : : * @param group The name of the group to add.
354 : : */
355 : 7 : void add_group(const string & group) {
356 : 7 : _groups.add(group);
357 : 7 : }
358 : :
359 : : /**
360 : : * Appends the contents of a GroupList to the end of the internal GroupList.
361 : : *
362 : : * @param groups The GroupList to append.
363 : : */
364 : : void add_groups(const GroupList & groups) {
365 : : _groups.add(groups);
366 : : }
367 : :
368 : : /**
369 : : * Returns the name of the group at the specified position in the
370 : : * internal Grouplist.
371 : : *
372 : : * @param index The index of the group name to return.
373 : : * @return The name of the group at the specified position in the
374 : : * internal Grouplist.
375 : : */
376 : 1 : string group(const unsigned int index) const {
377 : 1 : return _groups[index];
378 : : }
379 : :
380 : : /**
381 : : * Copies the internal GroupList.
382 : : *
383 : : * @param groups A reference to the GroupList that will store the copy.
384 : : */
385 : 2 : void copy_groups(GroupList & groups) {
386 : 2 : groups = _groups;
387 : 2 : }
388 : :
389 : : /**
390 : : * Returns the number of groups contained in the internal GroupList.
391 : : * @return The number of groups contained in the internal GroupList.
392 : : */
393 : : unsigned int count_groups() const {
394 : : return _groups.size();
395 : : }
396 : :
397 : : /**
398 : : * Clears the internal GroupList.
399 : : */
400 : 9 : void clear_groups() {
401 : 9 : _groups.clear();
402 : 9 : }
403 : :
404 : : /**
405 : : * Returns the number of message parts in the internal ScatterMessage.
406 : : * @return The number of message parts in the internal ScatterMessage.
407 : : */
408 : : unsigned int count_message_parts() const {
409 : : return _scatter.count_message_parts();
410 : : }
411 : :
412 : : /**
413 : : * Clears the internal ScatterMessage.
414 : : */
415 : 17 : void clear_message_parts() {
416 : 17 : _scatter.clear();
417 : 17 : }
418 : :
419 : : int send(const Message & message, const string & group)
420 : : SSRC_DECL_THROW(Error);
421 : :
422 : : int send(const Message & message, const GroupList & groups)
423 : : SSRC_DECL_THROW(Error);
424 : :
425 : : int send(const ScatterMessage & message, const GroupList & groups)
426 : : SSRC_DECL_THROW(Error);
427 : :
428 : : /**
429 : : * Same as send(message, _groups) where _groups is the internal
430 : : * GroupList containing only the supplied group parameter.
431 : : */
432 : 1 : int send(const ScatterMessage & message, const string & group)
433 : : SSRC_DECL_THROW(Error)
434 : : {
435 : 1 : clear_groups();
436 : 1 : add_group(group);
437 : 1 : return send(message, _groups);
438 : : }
439 : :
440 : : /**
441 : : * Same as send(_scatter, groups) where _scatter is the internal
442 : : * ScatterMessage after having its type and service set to to the
443 : : * supplied type and service values.
444 : : *
445 : : * @throw Error If the operation fails.
446 : : */
447 : 5 : int send(const GroupList & groups, const BaseMessage::message_type type = 0,
448 : : const BaseMessage::service_type service = BaseMessage::Safe)
449 : : SSRC_DECL_THROW(Error)
450 : : {
451 : 5 : _scatter.set_type(type);
452 : 5 : _scatter.set_service(service);
453 : 5 : return send(_scatter, groups);
454 : : }
455 : :
456 : : /**
457 : : * Same as send(_groups, type, service) where _groups is the internal
458 : : * GroupList containing only the supplied group parameter.
459 : : *
460 : : * @throw Error If the operation fails.
461 : : */
462 : 1 : int send(const string & group, const BaseMessage::message_type type = 0,
463 : : const BaseMessage::service_type service = BaseMessage::Safe)
464 : : SSRC_DECL_THROW(Error)
465 : : {
466 : 1 : clear_groups();
467 : 1 : add_group(group);
468 : 1 : return send(_groups, type, service);
469 : : }
470 : :
471 : : /**
472 : : * Same as send(_groups, type, service) where _groups is the internal
473 : : * GroupList.
474 : : *
475 : : * @throw Error If the operation fails.
476 : : */
477 : 4 : int send(const BaseMessage::message_type type = 0,
478 : : const BaseMessage::service_type service = BaseMessage::Safe)
479 : : SSRC_DECL_THROW(Error)
480 : : {
481 : 4 : return send(_groups, type, service);
482 : : }
483 : :
484 : : /**
485 : : * Same as send(messaage, _groups) where _groups is the internal GroupList.
486 : : *
487 : : * @throw Error If the operation fails.
488 : : */
489 : : int send(const Message & message) SSRC_DECL_THROW(Error) {
490 : : return send(message, _groups);
491 : : }
492 : :
493 : : /**
494 : : * Same as send(messaage, _groups) where _groups is the internal GroupList.
495 : : *
496 : : * @throw Error If the operation fails.
497 : : */
498 : : int send(const ScatterMessage & message) SSRC_DECL_THROW(Error) {
499 : : return send(message, _groups);
500 : : }
501 : :
502 : : int receive(ScatterMessage & message, GroupList & groups)
503 : : SSRC_DECL_THROW(BufferSizeError, Error);
504 : :
505 : : /**
506 : : * See receive(ScatterMessage &, GroupList &).
507 : : *
508 : : * @throw BufferSizeError
509 : : * @throw Error
510 : : */
511 : 7 : int receive(Message & message, GroupList & groups)
512 : : SSRC_DECL_THROW(BufferSizeError, Error)
513 : : {
514 : 7 : clear_message_parts();
515 : 7 : add_message_part(message);
516 : 7 : return receive(_scatter, groups);
517 : : }
518 : :
519 : : /**
520 : : * Same as receive(_scatter, groups), where _scatter is the internal
521 : : * ScatterMessage.
522 : : *
523 : : * @throw BufferSizeError
524 : : * @throw Error
525 : : */
526 : : int receive(GroupList & groups) SSRC_DECL_THROW(BufferSizeError, Error) {
527 : : return receive(_scatter, groups);
528 : : }
529 : :
530 : : /**
531 : : * Same as receive(message, _groups), where _groups is the interal
532 : : * GroupList.
533 : : *
534 : : * @throw BufferSizeError
535 : : * @throw Error
536 : : */
537 : 6 : int receive(Message & message) SSRC_DECL_THROW(BufferSizeError, Error) {
538 : 6 : return receive(message, _groups);
539 : : }
540 : :
541 : : /**
542 : : * Same as receive(message, _groups), where _groups is the interal
543 : : * GroupList.
544 : : *
545 : : * @throw BufferSizeError
546 : : * @throw Error
547 : : */
548 : : int receive(ScatterMessage & message)
549 : : SSRC_DECL_THROW(BufferSizeError, Error)
550 : : {
551 : : return receive(message, _groups);
552 : : }
553 : :
554 : : /**
555 : : * Same as receive(_scatter, _groups), where _scatter and _groups
556 : : * are the internal ScatterMessage and GroupList.
557 : : *
558 : : * @throw BufferSizeError
559 : : * @throw Error
560 : : */
561 : 2 : int receive() SSRC_DECL_THROW(BufferSizeError, Error) {
562 : 2 : return receive(_scatter, _groups);
563 : : }
564 : :
565 : : };
566 : :
567 : : __END_NS_SSRC_SPREAD
568 : :
569 : : #endif
|