2 * Copyright (c) 2005 Massachusetts Institute of Technology
4 * Permission is hereby granted, free of charge, to any person
5 * obtaining a copy of this software and associated documentation
6 * files (the "Software"), to deal in the Software without
7 * restriction, including without limitation the rights to use, copy,
8 * modify, merge, publish, distribute, sublicense, and/or sell copies
9 * of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 #ifndef __KHIMAIRA_KMQ_H__
28 #define __KHIMAIRA_KMQ_H__
30 /*! \defgroup kmq NetIDMgr Message Queue */
39 typedef DWORD kmq_thread_id;
40 typedef DWORD kmq_timer;
44 /*! \brief Window message for kmq
46 This message is sent to the window procedure of a window if that
47 window is a subscriber to KMQ messages.
49 \see kmq_subscribe_hwnd() for more information about handling this
52 #define KMQ_WM_DISPATCH (WM_APP+0x100)
57 /*! \brief A message callback
59 Should return TRUE if the message is properly handled. Otherwise
61 typedef khm_int32 (KHMAPI *kmq_callback_t)(khm_int32 msg_type,
62 khm_int32 msg_sub_type,
68 /*! \brief A single response.
70 Certain broadcast messages may user scatter-gather type
71 notification and result gathering. Individual subscribers to a
72 message attach their individual responses to a ::kmq_response
73 object and attach that to the message which can later be read by
74 the sender of the message.
76 typedef struct tag_kmq_response {
80 LDCL(struct tag_kmq_response);
83 /*! \brief A single message
85 typedef struct tag_kmq_message {
86 khm_int32 type; /*!< Type of message */
87 khm_int32 subtype; /*!< Subtype of message */
89 khm_ui_4 uparam; /*!< Integer parameter */
90 void * vparam; /*!< Pointer to parameter blob */
92 khm_int32 nSent; /*!< Number of instances of message
93 sent (for broadcast messages) */
95 khm_int32 nCompleted; /*!< Number of instances that have
96 completed processing (for broadcast
99 khm_int32 nFailed; /*!< Number of instances that failed
100 to process (for broadcast
103 kmq_response * responses; /*!< List of responses */
104 HANDLE wait_o; /*!< Event to wait on (only valid if
105 the publisher of the message
106 requested a handle to the call) */
108 kmq_timer timeSent; /*!< Time at which the message was
110 kmq_timer timeExpire; /*!< Time at which the message
113 kherr_context * err_ctx; /*!< Error context for the message */
117 LDCL(struct tag_kmq_message);
120 /*! \brief A handle to a call
122 typedef kmq_message *kmq_call;
124 /*! \brief Message reference */
125 typedef struct tag_kmq_message_ref {
126 kmq_message * msg; /*!< Message that we are referring
128 kmq_callback_t recipient; /*!< The recipient of the message */
130 LDCL(struct tag_kmq_message_ref);
133 /*! \brief Message queue
135 Each thread gets its own message queue. When a message is
136 broadcast to which there is a subscriber in a particular thread, a
137 reference to the message is placed in the message queue of the
138 thread. The dispatch procedure then dispatches the message as
139 described in the message reference.
141 typedef struct tag_kmq_queue {
142 kmq_thread_id thread; /*!< The thread id */
147 khm_int32 load; /*!< Number of messages waiting to be
148 processed on this message queue. */
149 kmq_timer last_post; /*!< Time the last message was
152 khm_int32 flags; /*!< Flags. Currently, it's just KMQ_QUEUE_FLAG_DELETED */
155 QDCL(kmq_message_ref); /*!< Queue of message references */
158 LDCL(struct tag_kmq_queue);
161 #define KMQ_QUEUE_FLAG_DELETED 0x0008
163 /*! \brief Message subscription
165 A subscription binds a recipient with a message type. These are
166 specific to a thread. I.e. a subscription that was made in one
167 thread will not receive messages in the context of another thread.
169 typedef struct tag_kmq_msg_subscription {
170 khm_int32 magic; /*!< Magic number. Should always be
171 ::KMQ_MSG_SUB_MAGIC */
172 khm_int32 type; /*!< Type of message */
173 khm_int32 rcpt_type; /*!< Type of recipient. One of
175 ::KMQ_RCPTTYPE_HWND */
177 kmq_callback_t cb; /*!< Callback if the subscription is
179 HWND hwnd; /*!< Window handle if the subscription
180 is a windows message type */
183 kmq_queue * queue; /*!< Associated queue */
186 LDCL(struct tag_kmq_msg_subscription);
187 } kmq_msg_subscription;
189 #define KMQ_MSG_SUB_MAGIC 0x3821b58e
191 /*! \brief Callback recipient type
193 The recipient is a callback function */
194 #define KMQ_RCPTTYPE_CB 1
196 /*! \brief Windows recipient type
198 The recipient is a window */
199 #define KMQ_RCPTTYPE_HWND 2
203 /*! \brief A completion handler for a message
205 Each message type can have a completion handler. Once a message
206 of this a specific type has been broadcast and handled by all the
207 subscripbers, the message will be passed down to the completion
208 handler before the associated data structures are freed. This
209 allows applications that define message type to also define clean
210 up for each message. For example, the completion handler can
211 initiate another message if the messages form a sequence or free
212 up blocks of memory that was passed as the parameter to the
215 typedef void (KHMAPI *kmq_msg_completion_handler)(kmq_message *);
217 /*! \brief A message type
219 typedef struct tag_kmq_msg_type {
220 khm_int32 id; /*!< Identifier for the message
222 kmq_msg_subscription * subs; /*!< The list of subscriptions */
223 kmq_msg_completion_handler completion_handler; /*!< Completion
224 handler for the message type */
226 wchar_t * name; /*!< Name of the message type for
227 named types. Message type names are
228 language independant. */
231 LDCL(struct tag_kmq_msg_type);
234 /*! \brief The maximum number of message types
236 #define KMQ_MSG_TYPE_MAX 255
238 /*! \brief Maximum number of characters in a message type name
240 The count includes the terminating NULL
242 #define KMQ_MAXCCH_TYPE_NAME 256
244 /*! \brief Maximum number of bytes in a message type name
246 Type count includes the terminating NULL
248 #define KMQ_MAXCB_TYPE_NAME (KMQ_MAXCCH_TYPE_NAME * sizeof(wchar_t))
250 KHMEXP khm_int32 KHMAPI kmq_init(void);
252 KHMEXP khm_int32 KHMAPI kmq_exit(void);
254 /*! \brief Register a message type
256 Registers a custom message type. The \a name parameter specifies
257 a language independent name for the message type and must be
258 unique and must be less than ::KMQ_MAXCCH_TYPE_NAME characters.
260 \param[in] name Name of the message type. Upto
261 ::KMQ_MAXCCH_TYPE_NAME characters including terminating NULL.
262 The \a name cannot be a zero length string.
264 \param[out] new_id Receives the new message type ID. Specify NULL
265 if the new message type is not required.
267 \see kmq_find_type() and kmq_unregister_type()
269 \retval KHM_ERROR_INVALID_PARAM The \a name parameter was invalid.
270 \retval KHM_ERROR_EXISTS A message type with that name already exists.
271 \retval KHM_ERROR_NO_RESOURCES Can't register any more message types.
272 \retval KHM_ERROR_SUCCESS The operation succeeded.
274 KHMEXP khm_int32 KHMAPI kmq_register_type(wchar_t * name, khm_int32 * new_id);
276 /*! \brief Find a message type
278 Find the message type with the given name. If found, the type ID
279 is returned in \a id.
281 \retval KHM_ERROR_SUCCESS A message type with the given name was
283 \retval KHM_ERROR_NOT_FOUND A message type with the given name was
286 KHMEXP khm_int32 KHMAPI kmq_find_type(wchar_t * name, khm_int32 * id);
288 /*! \brief Unregister a message type
290 Unregisters a message type that was registered using
293 \retval KHM_ERROR_SUCCESS The specified message type was
294 successfully unregistered.
296 \retval KHM_ERROR_NOT_FOUND The message type was not found.
298 KHMEXP khm_int32 KHMAPI kmq_unregister_type(khm_int32 id);
300 /*! \brief Subscribte to a message type.
302 Adds a subscription to messages of type \a type. Subscriptions
303 are managed per thread. Therefore the subscription is actually
304 added to the subscription list for the current thread (the thread
305 which calls kmq_subscribe()).
307 When a message of type \a type is received by the thread, it is
308 dispatched to the callback function identified by \a cb within the
309 context of this thread.
311 \note Calling kmq_subscribe() from within multiple threads with
312 the same \a type and \a cb will result in multiple
315 \see kmq_unsubscribe()
318 KHMEXP khm_int32 KHMAPI kmq_subscribe(khm_int32 type, kmq_callback_t cb);
320 /*! \brief Subscribe a window to a message type
322 Adds the window specified by \a hwnd to the subscription list for
323 the message type \a type. When a message of this type is posted,
324 then the window procedure of the window \a hwnd receives a message
327 When a window receives a ::KMQ_WM_DISPATCH message, it means that
328 a message has been posted which is of a type that the window has
329 subscribed for. Because of the way Windows handles window
330 messages and the way NetIDMgr message queues work, a thread which
331 has a window (or thread) procedure can not call kmq_dispatch() to
332 handle these messages. For threads that have window or thread
333 message loops, they must call kmq_subscribe_hwnd() to subscribe a
334 particular window (for thread message loops, this would be the
335 HWND of the message window for the thread) to NetIDMgr messages.
337 There are two supported ways of handling the ::KMQ_WM_DISPATCH
338 message. Examples of both are provided below.
340 Handling the message inline:
343 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
350 kmq_subscribe_hwnd(KMSG_CRED, hwnd);
356 kmq_unsubscribe_hwnd(KMSG_CRED, hwnd);
361 case KMQ_WM_DISPATCH:
362 kmq_wm_begin(lParam,&m);
364 if(m->type == KMSG_CRED && m->subtype == KMSG_CRED_ROOTDELTA) {
366 rv = KHM_ERROR_SUCCESS;
369 return kmq_wm_end(m, rv);
376 The other method is to dispatch the ::KMQ_WM_DISPATCH message to a
377 secondary callback function:
380 khm_int32 msg_handler(khm_int32 t, khm_int32 st, khm_ui_4 up, void * pb) {
381 khm_int32 rv = KHM_ERROR_SUCCESS;
388 LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
397 kmq_subscribe_hwnd(KMSG_CRED, hwnd);
403 kmq_unsubscribe_hwnd(KMSG_CRED, hwnd);
408 case KMQ_WM_DISPATCH:
409 return kmq_wm_dispatch(lParam, msg_handler);
416 \note Make sure you unsubscribe from the message type when the
419 \see kmq_unsubscribe_hwnd()
422 \see kmq_wm_dispatch()
424 KHMEXP khm_int32 KHMAPI kmq_subscribe_hwnd(khm_int32 type, HWND hwnd);
427 /*! \brief Begins handling a KMQ_WM_DISPATCH message
429 \return The return value of this function should be ignored.
431 \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH
433 KHMEXP LRESULT KHMAPI kmq_wm_begin(LPARAM lparm, kmq_message ** m);
435 /*! \brief Ends handling a KMQ_WM_DISPATCH message
437 \return The return value of this function should be the return
438 value of the window procedure. See kmq_subscribe_hwnd()
439 documentation for example
441 \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH
443 KHMEXP LRESULT KHMAPI kmq_wm_end(kmq_message *m, khm_int32 rv);
445 /*! \brief Dispatches a KMQ_WM_DISPATCH message to a callback
447 \return The return value of this function should be the return
448 value of the window procedure. See kmq_subscribe_hwnd()
449 documentation for example.
451 \see kmq_subscribe_hwnd() for more details about handling ::KMQ_WM_DISPATCH
453 KHMEXP LRESULT KHMAPI kmq_wm_dispatch(LPARAM lparm, kmq_callback_t cb);
456 /*! \brief Unsubscribe a callback from a message type
458 Removes the subscription for message type \a type for callback
459 function \a cb from the subscription list for the current thread
460 (the thread that calls kmq_unsubscribe()).
462 \note kmq_unsubscribe() can only remove subscriptions for the subscription
463 list for the current thread.
468 KHMEXP khm_int32 KHMAPI kmq_unsubscribe(khm_int32 type, kmq_callback_t cb);
470 /*! \brief Unsubscribe a window from a message type
472 Removes the specific window from the subsription list for message
475 \see kmq_subscribe_hwnd()
477 KHMEXP khm_int32 KHMAPI kmq_unsubscribe_hwnd(khm_int32 type, HWND hwnd);
479 /*! \brief Create an ad-hoc subscription
481 An ad-hoc subscription describes a callback point in a thread that
482 can be dispatched messages to individually without broadcasting.
484 \see kmq_post_sub_msg(), kmq_post_sub_msg_ex(),
485 kmq_send_sub_msg(), kmq_post_subs_msg(),
486 kmq_post_subs_msg_ex(), kmq_send_subs_msg(),
487 kmq_delete_subscription()
489 KHMEXP khm_int32 KHMAPI kmq_create_subscription(
491 khm_handle * result);
493 /*! \brief Create an ad-hoc subscription for a window
495 An ad-hoc subscription describes a window that will be dispatched
496 messages individually without broadcasting.
498 \see kmq_post_sub_msg(), kmq_post_sub_msg_ex(),
499 kmq_send_sub_msg(), kmq_post_subs_msg(),
500 kmq_post_subs_msg_ex(), kmq_send_subs_msg(),
501 kmq_delete_subscription()
503 KHMEXP khm_int32 KHMAPI kmq_create_hwnd_subscription(HWND hw,
504 khm_handle * result);
506 /*! \brief Delete an ad-hoc subscription
508 Deletes a subscriptoin that was created using
509 kmq_create_subscription()
511 KHMEXP khm_int32 KHMAPI kmq_delete_subscription(khm_handle sub);
513 /*! \brief Post a message to a subscription
515 Equivalent of kmq_post_msg() but only posts the message to the
516 specified subscription.
518 KHMEXP khm_int32 KHMAPI kmq_post_sub_msg(
525 /*! \brief Post a message to a subscription and acquire a handle to the call
527 KHMEXP khm_int32 KHMAPI kmq_post_sub_msg_ex(
535 /*! \brief Send a synchronous message to a subscription
537 \retval KHM_ERROR_SUCCESS The call succeeded, and no subscribers reported errors
538 \retval KHM_ERROR_PARTIAL The call succeeded, but at least one subscriber reported errors
540 KHMEXP khm_int32 KHMAPI kmq_send_sub_msg(
547 /*! \brief Post a message to a group of subscriptions
549 The block of memory pointed to by \a subs should be an array of
550 subscriptions. The number of elements in that array should be \a
551 n_subs. A message as specified by the remaining parameters will
552 be dispatched to all of the subscription points in the array.
554 KHMEXP khm_int32 KHMAPI kmq_post_subs_msg(
562 /*! \brief Post a message to a group of subscriptions and acquire a handle to the call
564 The block of memory pointed to by \a subs should be an array of
565 subscriptions. The number of elements in that array should be \a
566 n_subs. A message as specified by the remaining parameters will
567 be dispatched to all of the subscription points in the array, and
568 a handle to the call will be returned in \a call.
570 The returned \a call will reference all of the dispatches that
573 KHMEXP khm_int32 KHMAPI kmq_post_subs_msg_ex(
582 /*! \brief Send a synchronous message to a group of subscriptions
584 The block of memory pointed to by \a subs should be an array of
585 subscriptions. The number of elements in that array should be \a
586 n_subs. A message as specified by the remaining parameters will
587 be dispatched to all of the subscription points in the array. The
588 function will not return until all of the calls have succeeded.
590 \retval KHM_ERROR_SUCCESS The call succeeded, and no subscribers reported errors
591 \retval KHM_ERROR_PARTIAL The call succeeded, but at least one subscriber reported errors
593 KHMEXP khm_int32 KHMAPI kmq_send_subs_msg(
601 /*! \brief Dispatch a message for the current thread.
603 This function opens the message list for the current thread and
604 dispatches the first message instance that is found. Note that if
605 multiple callbacks subscribe to the same message type in the same
606 thread, then when a message of that type is received, multiple
607 message instances are added to the message queue corresponding to
610 If no message instances are waiting in the queue, kmq_dispatch()
611 waits for the \a timeout period for a message.
613 \param[in] timeout The timeout period in milliseconds. Specify INFINITE for
614 kmq_dispatch() to wait indefinitely.
616 \retval KHM_ERROR_SUCCESS A message instance was dispatched
617 \retval KHM_ERROR_TIMEOUT The timeout period elapsed
618 \retval KHM_ERROR_EXIT The message found on the queue was <KMSG_SYSTEM,KMSG_SYSTEM_EXIT>
620 KHMEXP khm_int32 KHMAPI kmq_dispatch(kmq_timer timeout);
622 /*! \brief Send a message
624 The specified message will be posted to all the subscribers of the
625 message type. Then the function will wait for all the subscribers
626 to finish processing the message before returning.
628 \param[in] type The type of the message
629 \param[in] subtype The subtype
630 \param[in] uparam The khm_ui_4 parameter for the message
631 \param[in] blob The parameter blob for the message
633 \note The internal timeout for this function is INFINITE. If you
634 it is desirable to use a different timeout, use
635 kmq_post_message_ex() and kmq_wait() functions.
637 \retval KHM_ERROR_SUCCESS The call succeeded and no subscribers returned errors
638 \retval KHM_ERROR_PARTIAL The call succeeded but at least one subscriber returned an error
640 KHMEXP khm_int32 KHMAPI kmq_send_message(
646 /*! \brief Post a message
648 The specified message will be posted to all the subscribers of the
649 message type. The function returns immediately.
651 If you want to be able to wait for all the subscribers to finish
652 processing the message, you should use kmq_post_message_ex()
655 \param[in] type The type of the message
656 \param[in] subtype The subtype
657 \param[in] uparam The khm_ui_4 parameter for the message
658 \param[in] blob The parameter blob for the message
660 KHMEXP khm_int32 KHMAPI kmq_post_message(
666 /*! \brief Post a message and acquire a handle to the call.
668 The specified message is posted to all the subscribers. In
669 addition, a handle is obtained for the call which can be used in
670 subsequent call to kmq_free_call() or kmq_wait().
672 Call kmq_free_call() to free the handle.
674 \param[in] type The type of the message
675 \param[in] subtype The subtype
676 \param[in] uparam The khm_ui_4 parameter for the message
677 \param[in] blob The parameter blob for the message
678 \param[out] call Receives the call handle. Set to NULL if the call handle is not required.
682 KHMEXP khm_int32 KHMAPI kmq_post_message_ex(
689 /*! \brief Free a handle to a call obtained through kmq_post_message_ex()
691 All call handles obtained through kmq_post_message_ex() must be
692 freed via a call to kmq_free_call().
694 KHMEXP khm_int32 KHMAPI kmq_free_call(kmq_call call);
696 /*! \brief Sends a <KMSG_SYSTEM,KMSG_SYSTEM_EXIT> message to the specified thread.
698 The message itself will not be received by any callback function,
699 however, any kmq_dispatch() function that is currently active of
700 becomes active will exit with a KHM_ERROR_EXIT code.
701 kmq_send_thread_quit_message() will wait for this to happen before
704 KHMEXP khm_int32 KHMAPI kmq_send_thread_quit_message(
705 kmq_thread_id thread,
708 /*! \brief Post a <KMSG_SYSTEM,KMSG_SYSTEM_EXIT> message to the specified thread.
710 The message itself will not be received by any callback function,
711 however, any kmq_dispatch() function that is currently active of
712 becomes active will exit with a KHM_ERROR_EXIT code.
713 kmq_post_thread_quit_message() will return immediately.
715 KHMEXP khm_int32 KHMAPI kmq_post_thread_quit_message(
716 kmq_thread_id thread,
720 KHMEXP khm_int32 KHMAPI kmq_get_next_response(kmq_call call, void ** resp);
722 /*! \brief Check if a specific call has completed
724 \return TRUE if the call has completed. FALSE otherwise.
726 KHMEXP khm_boolean KHMAPI kmq_has_completed(kmq_call call);
728 /*! \brief Wait for a call to complete.
730 Waits for the specified call to complete. If the call dispatched
731 to multiple recipients, the function waits for all dispatches to
734 If the call has already completed, then the function returns
737 If more than one thread is waiting for a single message to
738 complete, then only one of them will be released when the message
739 compeltes. Each subsequent thread will be released as each
740 released thread calls kmq_free_call().
742 \param[in] call A handle to a call.
743 \param[in] timeout Specifies, in milliseconds, the amount of time
744 to wait for the call to complete. Specify INFINITE to wait
747 \retval KHM_ERROR_SUCCESS The call completed
748 \retval KHM_ERROR_TIMEOUT The timeout period expired
749 \retval KHM_ERROR_INVALID_PARAM One of the parameters were invalid.
751 KHMEXP khm_int32 KHMAPI kmq_wait(kmq_call call, kmq_timer timeout);
753 /*! \brief Sets the completion handler for a specified message type.
755 \note Only one completion handler can exist for one message type.
756 Calling this function overwrites the previous completion
759 KHMEXP khm_int32 KHMAPI kmq_set_completion_handler(
761 kmq_msg_completion_handler hander);