The numbered list that follows has explanatory text that corresponds to the numbered comments in Example 4–2:
The global context data structure for this example. This is passed to mtaDequeueStart(), as the ctx1 call argument.
Per-thread data structure used by dequeue threads. While mtaDequeueStart() creates each dequeue thread, it is up to the process_message() routine to actually create any per-thread context it might need.
Initialize the global context before calling mtaDequeueStart().
Initiate dequeue processing by calling mtaDequeueStart(). The first call argument is a pointer to the global context. Each time mtaDequeueStart() calls process_message(), it passes in the global context pointer as the second argument. In this example, mtaDequeueStart() is not told to limit the number of dequeue threads it uses.
If the call to mtaDequeueStart() succeeds, the program exits normally.
If the call to mtaDequeueStart() fails, then a diagnostic error message is displayed and the program exits with an error status.
Each dequeue thread calls process_done() as it exits. This program cleans up and destroys any per-thread contexts created by the process_message() routine.
The program generates optional diagnostic output. Calling mtaLog() directs the output to the appropriate location: stdout if the program is run manually, and the channel log file if the program is run by the Job Controller.
mtaDequeueStart() calls process_message() once for each queued message to be processed. On the first call, the memory pointed at by my_ctx_2 is guaranteed to be NULL. The value of the first call argument passed to mtaDequeueStart() is passed to process_message() as the my_ctx_1 call argument.
The global context contains information pertinent to all the dequeue threads generated by the call mtaDequeueStart().
process_message() uses a per-thread context to save data across all calls to itself by a single dequeue thread.
mtaDequeueInfo() is used to obtain the envelope ID and RFC 1891 notification flags, if any, associated with the message being processed.
mtaDequeueRecipientNext() is used to obtain each envelope recipient address, one address per call. When there are no more recipient addresses to obtain, the routine returns the status MTA_EOF.
Each recipient is marked as delivered with a call to mtaDequeueRecipientDisposition(). An actual channel program would typically not make this call until after processing the message further.
If process_message() returns without dequeuing the message, mtaDequeueStart() defers the message for a later delivery attempt.
The message header and body are read one line at a time with mtaDequeueLineNext(). When there are no more lines to read, it returns a status of MTA_EOF.
Lines returned by mtaDequeueLineNext() might not be NULL terminated because the returned line pointer might point to a line in a read-only, memory-mapped file.
mtaDequeueMessageFinish() is called once the message had been fully processed and the disposition of all its recipients set with mtaDequeueRecipientDisposition(). The message is not truly dequeued until this happens.
The routine NotifyToStr() converts a bitmap encoded set of RFC 1891 notification flags to an ASCII text string.
The UniqueName() routine generates a unique string suitable for the use as a file name. This is used to generate the unique portion of the file name. This routine can be called concurrently by multiple threads and always generates a string unique amongst all processes and threads on the system.
For information on how to run this sample program, see Running Your Enqueue and Dequeue Programs.
The output that follows shows the result of 100 queued messages processed with the program in Example 4–2.
11:01:16.82: Dequeue thread starting: id=10; context=32360 11:01:16.87: Dequeue thread starting: id=1; context=32390 11:01:16.93: Dequeue thread starting: id=2; context=325e8 11:01:17.00: Dequeue thread starting: id=3; context=32600 11:01:17.04: Dequeue thread starting: id=4; context=32618 11:01:17.09: Dequeue thread starting: id=5; context=32630 11:01:17.14: Dequeue thread starting: id=6; context=78e50 11:01:17.19: Dequeue thread starting: id=7; context=88a18 11:01:17.23: Dequeue thread starting: id=9; context=8ab78 11:01:17.51: Dequeue thread starting: id=8; context=8ab60 11:01:19.96: Dequeue thread done: id=2; context=325e8; messages=12 11:01:19.96: Dequeue thread done: id=5; context=32630; messages=22 11:01:19.97: Dequeue thread done: id=6; context=78e50; messages=11 11:01:19.97: Dequeue thread done: id=4; context=32618; messages=5 11:01:19.98: Dequeue thread done: id=8; context=8ab60; messages=16 11:01:20.00: Dequeue thread done: id=9; context=8ab78; messages=5 11:01:20.00: Dequeue thread done: id=3; context=32600; messages=12 11:01:20.01: Dequeue thread done: id=1; context=32390; messages=7 11:01:20.02: Dequeue thread done: id=10; context=32360; messages=6 11:01:20.03: Dequeue thread done: id=7; context=88a18; messages=4