blob: 0fceb21444c288fb1bab827624454361747994cf [file] [log] [blame]
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +00001
2#ifdef WITH_SGI_DL
3#define USE_DL
4#endif
5
6#include <stdlib.h>
7#include <stdio.h>
8#include <signal.h>
9#include <sys/types.h>
10#include <sys/wait.h>
11#include <sys/prctl.h>
12#include <ulocks.h>
13#include <errno.h>
14
15#define HDR_SIZE 2680 /* sizeof(ushdr_t) */
16#define MAXPROC 100 /* max # of threads that can be started */
17
18static usptr_t *shared_arena;
19static ulock_t count_lock; /* protection for some variables */
20static ulock_t wait_lock; /* lock used to wait for other threads */
21static int waiting_for_threads; /* protected by count_lock */
22static int nthreads; /* protected by count_lock */
23static int exit_status;
24#ifndef NO_EXIT_PROG
25static int do_exit; /* indicates that the program is to exit */
26#endif
27static int exiting; /* we're already exiting (for maybe_exit) */
28static pid_t my_pid; /* PID of main thread */
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +000029static struct pidlist {
30 pid_t parent;
31 pid_t child;
32} pidlist[MAXPROC]; /* PIDs of other threads; protected by count_lock */
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000033static int maxpidindex; /* # of PIDs in pidlist */
34
35#ifndef NO_EXIT_PROG
36/*
37 * This routine is called as a signal handler when another thread
38 * exits. When that happens, we must see whether we have to exit as
Guido van Rossum65d5b571998-12-21 19:32:43 +000039 * well (because of an PyThread_exit_prog()) or whether we should continue on.
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000040 */
Thomas Woutersf70ef4f2000-07-22 18:47:25 +000041static void exit_sig(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000042{
43 d2printf(("exit_sig called\n"));
44 if (exiting && getpid() == my_pid) {
45 d2printf(("already exiting\n"));
46 return;
47 }
48 if (do_exit) {
49 d2printf(("exiting in exit_sig\n"));
Guido van Rossum408027e1996-12-30 16:17:54 +000050#ifdef Py_DEBUG
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000051 if ((thread_debug & 8) == 0)
52 thread_debug &= ~1; /* don't produce debug messages */
53#endif
Guido van Rossum65d5b571998-12-21 19:32:43 +000054 PyThread_exit_thread();
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000055 }
56}
57
58/*
59 * This routine is called when a process calls exit(). If that wasn't
Guido van Rossum65d5b571998-12-21 19:32:43 +000060 * done from the library, we do as if an PyThread_exit_prog() was intended.
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000061 */
Thomas Woutersf70ef4f2000-07-22 18:47:25 +000062static void maybe_exit(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000063{
64 dprintf(("maybe_exit called\n"));
65 if (exiting) {
66 dprintf(("already exiting\n"));
67 return;
68 }
Guido van Rossum65d5b571998-12-21 19:32:43 +000069 PyThread_exit_prog(0);
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000070}
71#endif /* NO_EXIT_PROG */
72
73/*
74 * Initialization.
75 */
Thomas Woutersf70ef4f2000-07-22 18:47:25 +000076static void PyThread__init_thread(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000077{
78#ifndef NO_EXIT_PROG
79 struct sigaction s;
80#endif /* NO_EXIT_PROG */
81#ifdef USE_DL
82 long addr, size;
83#endif /* USE_DL */
84
85
86#ifdef USE_DL
87 if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
88 perror("usconfig - CONF_INITSIZE (check)");
89 if (usconfig(CONF_INITSIZE, size) < 0)
90 perror("usconfig - CONF_INITSIZE (reset)");
91 addr = (long) dl_getrange(size + HDR_SIZE);
Fred Drakea44d3532000-06-30 15:01:00 +000092 dprintf(("trying to use addr %p-%p for shared arena\n", addr, addr+size));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +000093 errno = 0;
94 if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 && errno != 0)
95 perror("usconfig - CONF_ATTACHADDR (set)");
96#endif /* USE_DL */
97 if (usconfig(CONF_INITUSERS, 16) < 0)
98 perror("usconfig - CONF_INITUSERS");
99 my_pid = getpid(); /* so that we know which is the main thread */
100#ifndef NO_EXIT_PROG
101 atexit(maybe_exit);
102 s.sa_handler = exit_sig;
103 sigemptyset(&s.sa_mask);
104 /*sigaddset(&s.sa_mask, SIGUSR1);*/
105 s.sa_flags = 0;
106 sigaction(SIGUSR1, &s, 0);
107 if (prctl(PR_SETEXITSIG, SIGUSR1) < 0)
108 perror("prctl - PR_SETEXITSIG");
109#endif /* NO_EXIT_PROG */
110 if (usconfig(CONF_ARENATYPE, US_SHAREDONLY) < 0)
111 perror("usconfig - CONF_ARENATYPE");
112 usconfig(CONF_LOCKTYPE, US_DEBUG); /* XXX */
Guido van Rossum408027e1996-12-30 16:17:54 +0000113#ifdef Py_DEBUG
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000114 if (thread_debug & 4)
115 usconfig(CONF_LOCKTYPE, US_DEBUGPLUS);
116 else if (thread_debug & 2)
117 usconfig(CONF_LOCKTYPE, US_DEBUG);
Guido van Rossum408027e1996-12-30 16:17:54 +0000118#endif /* Py_DEBUG */
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000119 if ((shared_arena = usinit(tmpnam(0))) == 0)
120 perror("usinit");
121#ifdef USE_DL
122 if (usconfig(CONF_ATTACHADDR, addr) < 0) /* reset address */
123 perror("usconfig - CONF_ATTACHADDR (reset)");
124#endif /* USE_DL */
125 if ((count_lock = usnewlock(shared_arena)) == NULL)
126 perror("usnewlock (count_lock)");
127 (void) usinitlock(count_lock);
128 if ((wait_lock = usnewlock(shared_arena)) == NULL)
129 perror("usnewlock (wait_lock)");
Fred Drakea44d3532000-06-30 15:01:00 +0000130 dprintf(("arena start: %p, arena size: %ld\n", shared_arena, (long) usconfig(CONF_GETSIZE, shared_arena)));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000131}
132
133/*
134 * Thread support.
135 */
136
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000137static void clean_threads(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000138{
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000139 int i, j;
140 pid_t mypid, pid;
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000141
142 /* clean up any exited threads */
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000143 mypid = getpid();
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000144 i = 0;
145 while (i < maxpidindex) {
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000146 if (pidlist[i].parent == mypid && (pid = pidlist[i].child) > 0) {
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000147 pid = waitpid(pid, 0, WNOHANG);
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000148 if (pid > 0) {
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000149 /* a thread has exited */
150 pidlist[i] = pidlist[--maxpidindex];
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000151 /* remove references to children of dead proc */
152 for (j = 0; j < maxpidindex; j++)
153 if (pidlist[j].parent == pid)
154 pidlist[j].child = -1;
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000155 continue; /* don't increment i */
156 }
157 }
158 i++;
159 }
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000160 /* clean up the list */
161 i = 0;
162 while (i < maxpidindex) {
163 if (pidlist[i].child == -1) {
164 pidlist[i] = pidlist[--maxpidindex];
165 continue; /* don't increment i */
166 }
167 i++;
168 }
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000169}
170
Guido van Rossum3c288632001-10-16 21:13:49 +0000171long PyThread_start_new_thread(void (*func)(void *), void *arg)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000172{
173#ifdef USE_DL
174 long addr, size;
175 static int local_initialized = 0;
176#endif /* USE_DL */
177 int success = 0; /* init not needed when SOLARIS_THREADS and */
178 /* C_THREADS implemented properly */
179
Guido van Rossum65d5b571998-12-21 19:32:43 +0000180 dprintf(("PyThread_start_new_thread called\n"));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000181 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000182 PyThread_init_thread();
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000183 switch (ussetlock(count_lock)) {
184 case 0: return 0;
185 case -1: perror("ussetlock (count_lock)");
186 }
187 if (maxpidindex >= MAXPROC)
188 success = -1;
189 else {
190#ifdef USE_DL
191 if (!local_initialized) {
192 if ((size = usconfig(CONF_INITSIZE, 64*1024)) < 0)
193 perror("usconfig - CONF_INITSIZE (check)");
194 if (usconfig(CONF_INITSIZE, size) < 0)
195 perror("usconfig - CONF_INITSIZE (reset)");
196 addr = (long) dl_getrange(size + HDR_SIZE);
Fred Drakea44d3532000-06-30 15:01:00 +0000197 dprintf(("trying to use addr %p-%p for sproc\n",
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000198 addr, addr+size));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000199 errno = 0;
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000200 if ((addr = usconfig(CONF_ATTACHADDR, addr)) < 0 &&
201 errno != 0)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000202 perror("usconfig - CONF_ATTACHADDR (set)");
203 }
204#endif /* USE_DL */
205 clean_threads();
206 if ((success = sproc(func, PR_SALL, arg)) < 0)
207 perror("sproc");
208#ifdef USE_DL
209 if (!local_initialized) {
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000210 if (usconfig(CONF_ATTACHADDR, addr) < 0)
211 /* reset address */
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000212 perror("usconfig - CONF_ATTACHADDR (reset)");
213 local_initialized = 1;
214 }
215#endif /* USE_DL */
216 if (success >= 0) {
217 nthreads++;
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000218 pidlist[maxpidindex].parent = getpid();
219 pidlist[maxpidindex++].child = success;
220 dprintf(("pidlist[%d] = %d\n",
221 maxpidindex-1, success));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000222 }
223 }
224 if (usunsetlock(count_lock) < 0)
225 perror("usunsetlock (count_lock)");
Guido van Rossum3c288632001-10-16 21:13:49 +0000226 return success;
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000227}
228
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000229long PyThread_get_thread_ident(void)
Guido van Rossume944da81994-05-23 12:43:41 +0000230{
231 return getpid();
232}
233
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000234static void do_PyThread_exit_thread(int no_cleanup)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000235{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000236 dprintf(("PyThread_exit_thread called\n"));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000237 if (!initialized)
238 if (no_cleanup)
239 _exit(0);
240 else
241 exit(0);
242 if (ussetlock(count_lock) < 0)
243 perror("ussetlock (count_lock)");
244 nthreads--;
245 if (getpid() == my_pid) {
246 /* main thread; wait for other threads to exit */
247 exiting = 1;
248#ifndef NO_EXIT_PROG
249 if (do_exit) {
250 int i;
251
252 /* notify other threads */
253 clean_threads();
254 if (nthreads >= 0) {
255 dprintf(("kill other threads\n"));
256 for (i = 0; i < maxpidindex; i++)
Guido van Rossum6f5f8dd1994-05-30 13:39:05 +0000257 if (pidlist[i].child > 0)
258 (void) kill(pidlist[i].child,
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000259 SIGKILL);
260 _exit(exit_status);
261 }
262 }
263#endif /* NO_EXIT_PROG */
264 waiting_for_threads = 1;
265 if (ussetlock(wait_lock) < 0)
266 perror("ussetlock (wait_lock)");
267 for (;;) {
268 if (nthreads < 0) {
269 dprintf(("really exit (%d)\n", exit_status));
270 if (no_cleanup)
271 _exit(exit_status);
272 else
273 exit(exit_status);
274 }
275 if (usunsetlock(count_lock) < 0)
276 perror("usunsetlock (count_lock)");
277 dprintf(("waiting for other threads (%d)\n", nthreads));
278 if (ussetlock(wait_lock) < 0)
279 perror("ussetlock (wait_lock)");
280 if (ussetlock(count_lock) < 0)
281 perror("ussetlock (count_lock)");
282 }
283 }
284 /* not the main thread */
285 if (waiting_for_threads) {
286 dprintf(("main thread is waiting\n"));
287 if (usunsetlock(wait_lock) < 0)
288 perror("usunsetlock (wait_lock)");
289 }
290#ifndef NO_EXIT_PROG
291 else if (do_exit)
292 (void) kill(my_pid, SIGUSR1);
293#endif /* NO_EXIT_PROG */
294 if (usunsetlock(count_lock) < 0)
295 perror("usunsetlock (count_lock)");
296 _exit(0);
297}
298
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000299void PyThread_exit_thread(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000300{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000301 do_PyThread_exit_thread(0);
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000302}
303
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000304void PyThread__exit_thread(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000305{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000306 do_PyThread_exit_thread(1);
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000307}
308
309#ifndef NO_EXIT_PROG
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000310static void do_PyThread_exit_prog(int status, int no_cleanup)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000311{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000312 dprintf(("PyThread_exit_prog(%d) called\n", status));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000313 if (!initialized)
314 if (no_cleanup)
315 _exit(status);
316 else
317 exit(status);
318 do_exit = 1;
319 exit_status = status;
Guido van Rossum65d5b571998-12-21 19:32:43 +0000320 do_PyThread_exit_thread(no_cleanup);
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000321}
322
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000323void PyThread_exit_prog(int status)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000324{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000325 do_PyThread_exit_prog(status, 0);
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000326}
327
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000328void PyThread__exit_prog(int status)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000329{
Guido van Rossum65d5b571998-12-21 19:32:43 +0000330 do_PyThread_exit_prog(status, 1);
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000331}
332#endif /* NO_EXIT_PROG */
333
334/*
335 * Lock support.
336 */
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000337PyThread_type_lock PyThread_allocate_lock(void)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000338{
339 ulock_t lock;
340
Guido van Rossum65d5b571998-12-21 19:32:43 +0000341 dprintf(("PyThread_allocate_lock called\n"));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000342 if (!initialized)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000343 PyThread_init_thread();
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000344
345 if ((lock = usnewlock(shared_arena)) == NULL)
346 perror("usnewlock");
347 (void) usinitlock(lock);
Fred Drakea44d3532000-06-30 15:01:00 +0000348 dprintf(("PyThread_allocate_lock() -> %p\n", lock));
Guido van Rossum65d5b571998-12-21 19:32:43 +0000349 return (PyThread_type_lock) lock;
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000350}
351
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000352void PyThread_free_lock(PyThread_type_lock lock)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000353{
Fred Drakea44d3532000-06-30 15:01:00 +0000354 dprintf(("PyThread_free_lock(%p) called\n", lock));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000355 usfreelock((ulock_t) lock, shared_arena);
356}
357
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000358int PyThread_acquire_lock(PyThread_type_lock lock, int waitflag)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000359{
360 int success;
361
Fred Drakea44d3532000-06-30 15:01:00 +0000362 dprintf(("PyThread_acquire_lock(%p, %d) called\n", lock, waitflag));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000363 errno = 0; /* clear it just in case */
364 if (waitflag)
365 success = ussetlock((ulock_t) lock);
366 else
367 success = uscsetlock((ulock_t) lock, 1); /* Try it once */
368 if (success < 0)
369 perror(waitflag ? "ussetlock" : "uscsetlock");
Fred Drakea44d3532000-06-30 15:01:00 +0000370 dprintf(("PyThread_acquire_lock(%p, %d) -> %d\n", lock, waitflag, success));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000371 return success;
372}
373
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000374void PyThread_release_lock(PyThread_type_lock lock)
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000375{
Fred Drakea44d3532000-06-30 15:01:00 +0000376 dprintf(("PyThread_release_lock(%p) called\n", lock));
Guido van Rossum2c8cb9f1994-05-09 15:12:46 +0000377 if (usunsetlock((ulock_t) lock) < 0)
378 perror("usunsetlock");
379}
380
381/*
Guido van Rossuma027efa1997-05-05 20:56:21 +0000382 * Per-thread data ("key") support.
383 */
384
385struct key {
386 struct key *next;
387 long id;
388 int key;
389 void *value;
390};
391
392static struct key *keyhead = NULL;
393static int nkeys = 0;
Guido van Rossum65d5b571998-12-21 19:32:43 +0000394static PyThread_type_lock keymutex = NULL;
Guido van Rossuma027efa1997-05-05 20:56:21 +0000395
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000396static struct key *find_key(int key, void *value)
Guido van Rossuma027efa1997-05-05 20:56:21 +0000397{
398 struct key *p;
Guido van Rossum65d5b571998-12-21 19:32:43 +0000399 long id = PyThread_get_thread_ident();
Guido van Rossuma027efa1997-05-05 20:56:21 +0000400 for (p = keyhead; p != NULL; p = p->next) {
401 if (p->id == id && p->key == key)
402 return p;
403 }
404 if (value == NULL)
405 return NULL;
406 p = (struct key *)malloc(sizeof(struct key));
407 if (p != NULL) {
408 p->id = id;
409 p->key = key;
410 p->value = value;
Guido van Rossum65d5b571998-12-21 19:32:43 +0000411 PyThread_acquire_lock(keymutex, 1);
Guido van Rossuma027efa1997-05-05 20:56:21 +0000412 p->next = keyhead;
413 keyhead = p;
Guido van Rossum65d5b571998-12-21 19:32:43 +0000414 PyThread_release_lock(keymutex);
Guido van Rossuma027efa1997-05-05 20:56:21 +0000415 }
416 return p;
417}
418
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000419int PyThread_create_key(void)
Guido van Rossuma027efa1997-05-05 20:56:21 +0000420{
421 if (keymutex == NULL)
Guido van Rossum65d5b571998-12-21 19:32:43 +0000422 keymutex = PyThread_allocate_lock();
Guido van Rossuma027efa1997-05-05 20:56:21 +0000423 return ++nkeys;
424}
425
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000426void PyThread_delete_key(int key)
Guido van Rossuma027efa1997-05-05 20:56:21 +0000427{
428 struct key *p, **q;
Guido van Rossum65d5b571998-12-21 19:32:43 +0000429 PyThread_acquire_lock(keymutex, 1);
Guido van Rossuma027efa1997-05-05 20:56:21 +0000430 q = &keyhead;
431 while ((p = *q) != NULL) {
432 if (p->key == key) {
433 *q = p->next;
434 free((void *)p);
435 /* NB This does *not* free p->value! */
436 }
437 else
438 q = &p->next;
439 }
Guido van Rossum65d5b571998-12-21 19:32:43 +0000440 PyThread_release_lock(keymutex);
Guido van Rossuma027efa1997-05-05 20:56:21 +0000441}
442
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000443int PyThread_set_key_value(int key, void *value)
Guido van Rossuma027efa1997-05-05 20:56:21 +0000444{
445 struct key *p = find_key(key, value);
446 if (p == NULL)
447 return -1;
448 else
449 return 0;
450}
451
Thomas Woutersf70ef4f2000-07-22 18:47:25 +0000452void *PyThread_get_key_value(int key)
Guido van Rossuma027efa1997-05-05 20:56:21 +0000453{
454 struct key *p = find_key(key, NULL);
455 if (p == NULL)
456 return NULL;
457 else
458 return p->value;
459}