Книга: Programming with POSIX® Threads
6.6.5 SIGEV_THREAD
6.6.5 SIGEV_THREAD
Some of the functions in the POSLX.1b realtime standard, which provide for asynchronous notification, allow the programmer to give specific instructions about how that notification is to be accomplished. For example, when initiating an asynchronous device read or write using aio_read
or aio_write
, the programmer specifies a struct aiocb
, which contains, among other members, a struct sigevent
. Other functions that accept a struct sigevent
include timer_create
(which creates a per-process timer) and sigqueue
(which queues a signal to a process).
The struct sigevent
structure in POSIX.1b provides a "notification mechanism" that allows the programmer to specify whether a signal is to be generated, and, if so, what signal number should be used. Pthreads adds a new notification mechanism called SIGEV_THREAD. This new notification mechanism causes the signal notification function to be run as if it were the start routine of a thread.
Pthreads adds several members to the POSIX.1b struct sigevent
structure. The new members are sigev_notify_function
, a pointer to a thread start function; and sigev_notify_attributes
, a pointer to a thread attributes object (pthread_attr_t
) containing the desired thread creation attributes. If sigev_notify_attributes
is NULL, the notify thread is created as if the detachstate attribute was set to PTHREAD_CREATE_DETACHED. This avoids a memory leak — in general, the notify thread's identifier won't be available to any other thread. Furthermore, Pthreads says that the result of specifying an attributes object that has the detachstate attribute set to PTHREAD_CREATE_JOINABLE is "undefined." (Most likely, the result will be a memory leak because the thread cannot be joined — if you are lucky, the system may override your choice and create it detached anyway.)
The SIGEV_THREAD notification function may not actually be run in a new thread — Pthreads carefully specifies that it behaves as if it were run in a new thread, just as I did a few paragraphs ago. The system may, for example, queue SIGEV_THREAD events and call the start routines, serially, in some internal "server thread." The difference is effectively indistinguishable to the application. A system that uses a server thread must be very careful about the attributes specified for the notification thread—for example, scheduling policy and priority, contention scope, and minimum stack size must all be taken into consideration.
The SIGEV_THREAD feature is not available to any of the "traditional" signal generation mechanisms, such as setitimer
, or for SIGCHLD, SIGINT, and so forth. Those who are programming using the POSIX.1b "realtime signal" interfaces, including timers and asynchronous I/O, may find this new capability useful.
The following program, sigev_thread.c, shows how to use the SIGEV_THREAD notification mechanism for a POSIX.1b timer.
20-37 The function timer_thread is specified as the "notification function" (thread start routine) for the SIGEV_THREAD timer. The function will be called each time the timer expires. It counts expirations, and wakes the main thread after five. Notice that, unlike a signal-catching function, the SIGEV_THREAD notification function can make full use of Pthreads synchronization operations. This can be a substantial advantage in many situations.
45-51 Unfortunately, neither Solaris 2.5 nor Digital UNIX 4.0 correctly implemented SIGEV_THREAD. Thus, unlike all other examples in this book, this code will not compile on Solaris 2.5. This #ifdef block allows the code to compile, and to fail gracefully if the resulting program is run, with an error message. Although the program will compile on Digital UNIX 4.0, it will not run. The implementation of SIGEV_THREAD has been fixed in Digital UNIX 4.0D, which should be available by the time you read this, and it should also be fixed in Solaris 2.6.
56-59 These statements initialize the sigevent structure, which describes how the system should notify the application when an event occurs. In this case, we are telling it to call timer_thread when the timer expires, and to use default attributes.
? sigev_thread.c
1 #include <pthread.h>
2 #include <sys/signal.h>
3 #include <sys/time.h>
4 #include "errors.h"
5
6 timer_t timer_id;
7 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
8 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
9 int counter = 0;
10
11 /*
12 * Thread start routine to notify the application when the
13 * timer expires. This routine is run "as if" it were a new
14 * thread, each time the timer expires.
15 *
16 * When the timer has expired 5 times, the main thread will
17 * be awakened, and will terminate the program.
18 */
19 void
20 timer_thread (void *arg)
21 {
22 int status;
23
24 status = pthread_mutex_lock (&mutex);
25 if (status != 0)
26 err_abort (status, "Lock mutex");
27 if (++counter >= 5) {
28 status = pthread_cond_signal (&cond);
29 if (status != 0)
30 err_abort (status, "Signal condition");
31 }
32 status = pthread_mutex_unlock (&mutex);
33 if (status != 0)
34 err_abort (status, "Unlock mutex");
35
36 printf ("Timer %dn", counter);
37 }
38
39 main( )
40 {
41 int status;
42 struct itimerspec ts;
43 struct sigevent se;
44
45 #ifdef sun
46 fprintf (
47 stderr,
48 "This program cannot compile on Solaris 2.5.n"
49 "To build and run on Solaris 2.6, remove then"
50 ""#ifdef sun" block in main().n");
51 #else
52 /*
53 * Set the sigevent structure to cause the signal to be
54 * delivered by creating a new thread.
55 */
56 se.sigev_notify = SIGEV_THREAD;
57 se.sigev_value.sival_ptr = &timer_id;
58 se.sigev_notify_function = timer_thread;
59 se.sigev_notify_attributes = NULL;
60
61 /*
62 * Specify a repeating timer that fires every 5 seconds.
63 */
64 ts.it_value.tv_sec = 5;
65 ts.it_value.tv_nsec = 0;
66 ts.it_interval.tv_sec = 5;
67 ts.it_interval.tv_nsec = 0;
68
69 DPRINTF (("Creating timern"));
70 status = timer_create(CLOCK_REALTIME, &se, &timer_id);
71 if (status == -1)
72 errno_abort ("Create timer");
73
74 DPRINTF ((
75 "Setting timer %d for 5-second expiration...n", timer_id));
76 status = timer_settime(timer_id, 0, &ts, 0);
77 if (status == -1)
78 errno_abort ("Set timer");
79
80 status = pthread_mutex_lock (&mutex);
81 if (status != 0)
82 err_abort (status, "Lock mutex");
83 while (counter < 5) {
84 status = pthread_cond_wait (&cond, &mutex);
85 if (status != 0)
86 err_abort (status, "Wait on condition");
87 }
88 status = pthread_mutex_unlock (&mutex);
89 if (status != 0)
90 err_abort (status, "Unlock mutex");
91
92 #endif /* Sun */
93 return 0;
94 }
- Thread Synchronization
- Тайм-ауты ядра и функция pthread_join()
- Thread Management API
- 4 A few ways to use threads
- Функция pthread_rwlock_trywrlock
- 5.5. The init Thread
- Thread resources on the Internet
- 15.4.2. Debugging Multithreaded Applications
- 4.1. THREADS
- 4.1.1. Introduction to Threads
- 4.1.2. Thread Usage
- 4.1.3. Design Issues for Threads Packages