1
2#include <stdlib.h>
3#include <stdio.h>
4#include <signal.h>
5#include <sys/types.h>
6#include <sys/wait.h>
7#include <sys/prctl.h>
8#include <ulocks.h>
9#include <errno.h>
10
11#define HDR_SIZE        2680    /* sizeof(ushdr_t) */
12#define MAXPROC         100     /* max # of threads that can be started */
13
14static usptr_t *shared_arena;
15static ulock_t count_lock;      /* protection for some variables */
16static ulock_t wait_lock;       /* lock used to wait for other threads */
17static int waiting_for_threads; /* protected by count_lock */
18static int nthreads;            /* protected by count_lock */
19static int exit_status;
20static int exiting;             /* we're already exiting (for maybe_exit) */
21static pid_t my_pid;            /* PID of main thread */
22static struct pidlist {
23    pid_t parent;
24    pid_t child;
25} pidlist[MAXPROC];     /* PIDs of other threads; protected by count_lock */
26static int maxpidindex;         /* # of PIDs in pidlist */
27/*
28 * Initialization.
29 */
30static void PyThread__init_thread(void)
31{
32#ifdef USE_DL
33    long addr, size;
34#endif /* USE_DL */
35
36
37#ifdef USE_DL
38    if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
39        perror("usconfig - CONF_INITSIZE (check)");
40    if (usconfig(CONF_INITSIZE, size) < 0)
41        perror("usconfig - CONF_INITSIZE (reset)");
42    addr = (long) dl_getrange(size + HDR_SIZE);
43    dprintf(("trying to use addr %p-%p for shared arena\n", addr, addr+size));
44    errno = 0;
45    if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0)
46        perror("usconfig - CONF_ATTACHADDR (set)");
47#endif /* USE_DL */
48    if (usconfig(CONF_INITUSERS, 16) < 0)
49        perror("usconfig - CONF_INITUSERS");
50    my_pid = getpid();          /* so that we know which is the main thread */
51    if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0)
52        perror("usconfig - CONF_ARENATYPE");
53    usconfig(CONF_LOCKTYPE, US_DEBUG); /* XXX */
54#ifdef Py_DEBUG
55    if (thread_debug & 4)
56        usconfig(CONF_LOCKTYPE, US_DEBUGPLUS);
57    else if (thread_debug & 2)
58        usconfig(CONF_LOCKTYPE, US_DEBUG);
59#endif /* Py_DEBUG */
60    if ((shared_arena = usinit(tmpnam(0))) == 0)
61        perror("usinit");
62#ifdef USE_DL
63    if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */
64        perror("usconfig - CONF_ATTACHADDR (reset)");
65#endif /* USE_DL */
66    if ((count_lock = usnewlock(shared_arena)) == NULL)
67        perror("usnewlock (count_lock)");
68    (void) usinitlock(count_lock);
69    if ((wait_lock = usnewlock(shared_arena)) == NULL)
70        perror("usnewlock (wait_lock)");
71    dprintf(("arena start: %p, arena size: %ld\n",  shared_arena, (long) usconfig(CONF_GETSIZE, shared_arena)));
72}
73
74/*
75 * Thread support.
76 */
77
78static void clean_threads(void)
79{
80    int i, j;
81    pid_t mypid, pid;
82
83    /* clean up any exited threads */
84    mypid = getpid();
85    i = 0;
86    while (i < maxpidindex) {
87        if (pidlist[i].parent == mypid && (pid = pidlist[i].child) > 0) {
88            pid = waitpid(pid, 0, WNOHANG);
89            if (pid > 0) {
90                /* a thread has exited */
91                pidlist[i] = pidlist[--maxpidindex];
92                /* remove references to children of dead proc */
93                for (j = 0; j < maxpidindex; j++)
94                    if (pidlist[j].parent == pid)
95                        pidlist[j].child = -1;
96                continue; /* don't increment i */
97            }
98        }
99        i++;
100    }
101    /* clean up the list */
102    i = 0;
103    while (i < maxpidindex) {
104        if (pidlist[i].child == -1) {
105            pidlist[i] = pidlist[--maxpidindex];
106            continue; /* don't increment i */
107        }
108        i++;
109    }
110}
111
112long PyThread_start_new_thread(void (*func)(void *), void *arg)
113{
114#ifdef USE_DL
115    long addr, size;
116    static int local_initialized = 0;
117#endif /* USE_DL */
118    int success = 0;            /* init not needed when SOLARIS_THREADS and */
119                /* C_THREADS implemented properly */
120
121    dprintf(("PyThread_start_new_thread called\n"));
122    if (!initialized)
123        PyThread_init_thread();
124    switch (ussetlock(count_lock)) {
125    case 0: return 0;
126    case -1: perror("ussetlock (count_lock)");
127    }
128    if (maxpidindex >= MAXPROC)
129        success = -1;
130    else {
131#ifdef USE_DL
132        if (!local_initialized) {
133            if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
134                perror("usconfig - CONF_INITSIZE (check)");
135            if (usconfig(CONF_INITSIZE, size) < 0)
136                perror("usconfig - CONF_INITSIZE (reset)");
137            addr = (long) dl_getrange(size + HDR_SIZE);
138            dprintf(("trying to use addr %p-%p for sproc\n",
139                     addr, addr+size));
140            errno = 0;
141            if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 &&
142                errno != 0)
143                perror("usconfig - CONF_ATTACHADDR (set)");
144        }
145#endif /* USE_DL */
146        clean_threads();
147        if ((success = sproc(func, PR_SALL, arg)) < 0)
148            perror("sproc");
149#ifdef USE_DL
150        if (!local_initialized) {
151            if (usconfig(CONF_ATTACHADDR, addr) < 0)
152                /* reset address */
153                perror("usconfig - CONF_ATTACHADDR (reset)");
154            local_initialized = 1;
155        }
156#endif /* USE_DL */
157        if (success >= 0) {
158            nthreads++;
159            pidlist[maxpidindex].parent = getpid();
160            pidlist[maxpidindex++].child = success;
161            dprintf(("pidlist[%d] = %d\n",
162                     maxpidindex-1, success));
163        }
164    }
165    if (usunsetlock(count_lock) < 0)
166        perror("usunsetlock (count_lock)");
167    return success;
168}
169
170long PyThread_get_thread_ident(void)
171{
172    return getpid();
173}
174
175void PyThread_exit_thread(void)
176{
177    dprintf(("PyThread_exit_thread called\n"));
178    if (!initialized)
179        exit(0);
180    if (ussetlock(count_lock) < 0)
181        perror("ussetlock (count_lock)");
182    nthreads--;
183    if (getpid() == my_pid) {
184        /* main thread; wait for other threads to exit */
185        exiting = 1;
186        waiting_for_threads = 1;
187        if (ussetlock(wait_lock) < 0)
188            perror("ussetlock (wait_lock)");
189        for (;;) {
190            if (nthreads < 0) {
191                dprintf(("really exit (%d)\n", exit_status));
192                exit(exit_status);
193            }
194            if (usunsetlock(count_lock) < 0)
195                perror("usunsetlock (count_lock)");
196            dprintf(("waiting for other threads (%d)\n", nthreads));
197            if (ussetlock(wait_lock) < 0)
198                perror("ussetlock (wait_lock)");
199            if (ussetlock(count_lock) < 0)
200                perror("ussetlock (count_lock)");
201        }
202    }
203    /* not the main thread */
204    if (waiting_for_threads) {
205        dprintf(("main thread is waiting\n"));
206        if (usunsetlock(wait_lock) < 0)
207            perror("usunsetlock (wait_lock)");
208    }
209    if (usunsetlock(count_lock) < 0)
210        perror("usunsetlock (count_lock)");
211    _exit(0);
212}
213
214/*
215 * Lock support.
216 */
217PyThread_type_lock PyThread_allocate_lock(void)
218{
219    ulock_t lock;
220
221    dprintf(("PyThread_allocate_lock called\n"));
222    if (!initialized)
223        PyThread_init_thread();
224
225    if ((lock = usnewlock(shared_arena)) == NULL)
226        perror("usnewlock");
227    (void) usinitlock(lock);
228    dprintf(("PyThread_allocate_lock() -> %p\n", lock));
229    return (PyThread_type_lock) lock;
230}
231
232void PyThread_free_lock(PyThread_type_lock lock)
233{
234    dprintf(("PyThread_free_lock(%p) called\n", lock));
235    usfreelock((ulock_t) lock, shared_arena);
236}
237
238int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
239{
240    int success;
241
242    dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
243    errno = 0;                  /* clear it just in case */
244    if (waitflag)
245        success = ussetlock((ulock_t) lock);
246    else
247        success = uscsetlock((ulock_t) lock, 1); /* Try it once */
248    if (success < 0)
249        perror(waitflag ? "ussetlock" : "uscsetlock");
250    dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
251    return success;
252}
253
254void PyThread_release_lock(PyThread_type_lock lock)
255{
256    dprintf(("PyThread_release_lock(%p) called\n", lock));
257    if (usunsetlock((ulock_t) lock) < 0)
258        perror("usunsetlock");
259}
260