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