12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192 |
- /* Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004,
- * 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
- * 2014 Free Software Foundation, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of
- * the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301 USA
- */
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include "libguile/bdw-gc.h"
- #include <gc/gc_mark.h>
- #include "libguile/_scm.h"
- #include <stdlib.h>
- #include <unistd.h>
- #include <stdio.h>
- #ifdef HAVE_STRING_H
- #include <string.h> /* for memset used by FD_ZERO on Solaris 10 */
- #endif
- #if HAVE_SYS_TIME_H
- #include <sys/time.h>
- #endif
- #if HAVE_PTHREAD_NP_H
- # include <pthread_np.h>
- #endif
- #include <sys/select.h>
- #include <assert.h>
- #include <fcntl.h>
- #include <nproc.h>
- #include "libguile/validate.h"
- #include "libguile/root.h"
- #include "libguile/eval.h"
- #include "libguile/async.h"
- #include "libguile/ports.h"
- #include "libguile/threads.h"
- #include "libguile/dynwind.h"
- #include "libguile/iselect.h"
- #include "libguile/fluids.h"
- #include "libguile/continuations.h"
- #include "libguile/gc.h"
- #include "libguile/gc-inline.h"
- #include "libguile/init.h"
- #include "libguile/scmsigs.h"
- #include "libguile/strings.h"
- #include "libguile/vm.h"
- #include <full-read.h>
- /* The GC "kind" for threads that allow them to mark their VM
- stacks. */
- static int thread_gc_kind;
- static struct GC_ms_entry *
- thread_mark (GC_word *addr, struct GC_ms_entry *mark_stack_ptr,
- struct GC_ms_entry *mark_stack_limit, GC_word env)
- {
- int word;
- const struct scm_i_thread *t = (struct scm_i_thread *) addr;
- if (SCM_UNPACK (t->handle) == 0)
- /* T must be on the free-list; ignore. (See warning in
- gc_mark.h.) */
- return mark_stack_ptr;
- /* Mark T. We could be more precise, but it doesn't matter. */
- for (word = 0; word * sizeof (*addr) < sizeof (*t); word++)
- mark_stack_ptr = GC_MARK_AND_PUSH ((void *) addr[word],
- mark_stack_ptr, mark_stack_limit,
- NULL);
- /* The pointerless freelists are threaded through their first word,
- but GC doesn't know to trace them (as they are pointerless), so we
- need to do that here. See the comments at the top of libgc's
- gc_inline.h. */
- if (t->pointerless_freelists)
- {
- size_t n;
- for (n = 0; n < SCM_INLINE_GC_FREELIST_COUNT; n++)
- {
- void *chain = t->pointerless_freelists[n];
- if (chain)
- {
- /* The first link is already marked by the freelist vector,
- so we just have to mark the tail. */
- while ((chain = *(void **)chain))
- mark_stack_ptr = GC_mark_and_push (chain, mark_stack_ptr,
- mark_stack_limit, NULL);
- }
- }
- }
- if (t->vp)
- mark_stack_ptr = scm_i_vm_mark_stack (t->vp, mark_stack_ptr,
- mark_stack_limit);
- return mark_stack_ptr;
- }
- static void
- to_timespec (SCM t, scm_t_timespec *waittime)
- {
- if (scm_is_pair (t))
- {
- waittime->tv_sec = scm_to_ulong (SCM_CAR (t));
- waittime->tv_nsec = scm_to_ulong (SCM_CDR (t)) * 1000;
- }
- else
- {
- double time = scm_to_double (t);
- double sec = scm_c_truncate (time);
- waittime->tv_sec = (long) sec;
- waittime->tv_nsec = (long) ((time - sec) * 1000000000);
- }
- }
- /*** Queues */
- /* Note: We annotate with "GC-robust" assignments whose purpose is to avoid
- the risk of false references leading to unbounded retained space as
- described in "Bounding Space Usage of Conservative Garbage Collectors",
- H.J. Boehm, 2001. */
- /* Make an empty queue data structure.
- */
- static SCM
- make_queue ()
- {
- return scm_cons (SCM_EOL, SCM_EOL);
- }
- /* Put T at the back of Q and return a handle that can be used with
- remqueue to remove T from Q again.
- */
- static SCM
- enqueue (SCM q, SCM t)
- {
- SCM c = scm_cons (t, SCM_EOL);
- SCM_CRITICAL_SECTION_START;
- if (scm_is_null (SCM_CDR (q)))
- SCM_SETCDR (q, c);
- else
- SCM_SETCDR (SCM_CAR (q), c);
- SCM_SETCAR (q, c);
- SCM_CRITICAL_SECTION_END;
- return c;
- }
- /* Remove the element that the handle C refers to from the queue Q. C
- must have been returned from a call to enqueue. The return value
- is zero when the element referred to by C has already been removed.
- Otherwise, 1 is returned.
- */
- static int
- remqueue (SCM q, SCM c)
- {
- SCM p, prev = q;
- SCM_CRITICAL_SECTION_START;
- for (p = SCM_CDR (q); !scm_is_null (p); p = SCM_CDR (p))
- {
- if (scm_is_eq (p, c))
- {
- if (scm_is_eq (c, SCM_CAR (q)))
- SCM_SETCAR (q, scm_is_eq (prev, q) ? SCM_EOL : prev);
- SCM_SETCDR (prev, SCM_CDR (c));
- /* GC-robust */
- SCM_SETCDR (c, SCM_EOL);
- SCM_CRITICAL_SECTION_END;
- return 1;
- }
- prev = p;
- }
- SCM_CRITICAL_SECTION_END;
- return 0;
- }
- /* Remove the front-most element from the queue Q and return it.
- Return SCM_BOOL_F when Q is empty.
- */
- static SCM
- dequeue (SCM q)
- {
- SCM c;
- SCM_CRITICAL_SECTION_START;
- c = SCM_CDR (q);
- if (scm_is_null (c))
- {
- SCM_CRITICAL_SECTION_END;
- return SCM_BOOL_F;
- }
- else
- {
- SCM_SETCDR (q, SCM_CDR (c));
- if (scm_is_null (SCM_CDR (q)))
- SCM_SETCAR (q, SCM_EOL);
- SCM_CRITICAL_SECTION_END;
- /* GC-robust */
- SCM_SETCDR (c, SCM_EOL);
- return SCM_CAR (c);
- }
- }
- /*** Thread smob routines */
- static int
- thread_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
- {
- /* On a Gnu system pthread_t is an unsigned long, but on mingw it's a
- struct. A cast like "(unsigned long) t->pthread" is a syntax error in
- the struct case, hence we go via a union, and extract according to the
- size of pthread_t. */
- union {
- scm_i_pthread_t p;
- unsigned short us;
- unsigned int ui;
- unsigned long ul;
- scm_t_uintmax um;
- } u;
- scm_i_thread *t = SCM_I_THREAD_DATA (exp);
- scm_i_pthread_t p = t->pthread;
- scm_t_uintmax id;
- u.p = p;
- if (sizeof (p) == sizeof (unsigned short))
- id = u.us;
- else if (sizeof (p) == sizeof (unsigned int))
- id = u.ui;
- else if (sizeof (p) == sizeof (unsigned long))
- id = u.ul;
- else
- id = u.um;
- scm_puts_unlocked ("#<thread ", port);
- scm_uintprint (id, 10, port);
- scm_puts_unlocked (" (", port);
- scm_uintprint ((scm_t_bits)t, 16, port);
- scm_puts_unlocked (")>", port);
- return 1;
- }
- /*** Blocking on queues. */
- /* See also scm_i_queue_async_cell for how such a block is
- interrputed.
- */
- /* Put the current thread on QUEUE and go to sleep, waiting for it to
- be woken up by a call to 'unblock_from_queue', or to be
- interrupted. Upon return of this function, the current thread is
- no longer on QUEUE, even when the sleep has been interrupted.
- The caller of block_self must hold MUTEX. It will be atomically
- unlocked while sleeping, just as with scm_i_pthread_cond_wait.
- SLEEP_OBJECT is an arbitrary SCM value that is kept alive as long
- as MUTEX is needed.
- When WAITTIME is not NULL, the sleep will be aborted at that time.
- The return value of block_self is an errno value. It will be zero
- when the sleep has been successfully completed by a call to
- unblock_from_queue, EINTR when it has been interrupted by the
- delivery of a system async, and ETIMEDOUT when the timeout has
- expired.
- The system asyncs themselves are not executed by block_self.
- */
- static int
- block_self (SCM queue, SCM sleep_object, scm_i_pthread_mutex_t *mutex,
- const scm_t_timespec *waittime)
- {
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- SCM q_handle;
- int err;
- if (scm_i_setup_sleep (t, sleep_object, mutex, -1))
- err = EINTR;
- else
- {
- t->block_asyncs++;
- q_handle = enqueue (queue, t->handle);
- if (waittime == NULL)
- err = scm_i_scm_pthread_cond_wait (&t->sleep_cond, mutex);
- else
- err = scm_i_scm_pthread_cond_timedwait (&t->sleep_cond, mutex, waittime);
- /* When we are still on QUEUE, we have been interrupted. We
- report this only when no other error (such as a timeout) has
- happened above.
- */
- if (remqueue (queue, q_handle) && err == 0)
- err = EINTR;
- t->block_asyncs--;
- scm_i_reset_sleep (t);
- }
- return err;
- }
- /* Wake up the first thread on QUEUE, if any. The awoken thread is
- returned, or #f if the queue was empty.
- */
- static SCM
- unblock_from_queue (SCM queue)
- {
- SCM thread = dequeue (queue);
- if (scm_is_true (thread))
- scm_i_pthread_cond_signal (&SCM_I_THREAD_DATA(thread)->sleep_cond);
- return thread;
- }
- /* Getting into and out of guile mode.
- */
- /* Key used to attach a cleanup handler to a given thread. Also, if
- thread-local storage is unavailable, this key is used to retrieve the
- current thread with `pthread_getspecific ()'. */
- scm_i_pthread_key_t scm_i_thread_key;
- #ifdef SCM_HAVE_THREAD_STORAGE_CLASS
- /* When thread-local storage (TLS) is available, a pointer to the
- current-thread object is kept in TLS. Note that storing the thread-object
- itself in TLS (rather than a pointer to some malloc'd memory) is not
- possible since thread objects may live longer than the actual thread they
- represent. */
- SCM_THREAD_LOCAL scm_i_thread *scm_i_current_thread = NULL;
- #endif /* SCM_HAVE_THREAD_STORAGE_CLASS */
- static scm_i_pthread_mutex_t thread_admin_mutex = SCM_I_PTHREAD_MUTEX_INITIALIZER;
- static scm_i_thread *all_threads = NULL;
- static int thread_count;
- static SCM scm_i_default_dynamic_state;
- /* Run when a fluid is collected. */
- void
- scm_i_reset_fluid (size_t n)
- {
- scm_i_thread *t;
- scm_i_pthread_mutex_lock (&thread_admin_mutex);
- for (t = all_threads; t; t = t->next_thread)
- if (SCM_I_DYNAMIC_STATE_P (t->dynamic_state))
- {
- SCM v = SCM_I_DYNAMIC_STATE_FLUIDS (t->dynamic_state);
-
- if (n < SCM_SIMPLE_VECTOR_LENGTH (v))
- SCM_SIMPLE_VECTOR_SET (v, n, SCM_UNDEFINED);
- }
- scm_i_pthread_mutex_unlock (&thread_admin_mutex);
- }
- /* Perform first stage of thread initialisation, in non-guile mode.
- */
- static void
- guilify_self_1 (struct GC_stack_base *base)
- {
- scm_i_thread t;
- /* We must arrange for SCM_I_CURRENT_THREAD to point to a valid value
- before allocating anything in this thread, because allocation could
- cause GC to run, and GC could cause finalizers, which could invoke
- Scheme functions, which need the current thread to be set. */
- t.pthread = scm_i_pthread_self ();
- t.handle = SCM_BOOL_F;
- t.result = SCM_BOOL_F;
- t.cleanup_handler = SCM_BOOL_F;
- t.mutexes = SCM_EOL;
- t.held_mutex = NULL;
- t.join_queue = SCM_EOL;
- t.freelists = NULL;
- t.pointerless_freelists = NULL;
- t.dynamic_state = SCM_BOOL_F;
- t.dynstack.base = NULL;
- t.dynstack.top = NULL;
- t.dynstack.limit = NULL;
- t.active_asyncs = SCM_EOL;
- t.block_asyncs = 1;
- t.pending_asyncs = 1;
- t.critical_section_level = 0;
- t.base = base->mem_base;
- #ifdef __ia64__
- t.register_backing_store_base = base->reg_base;
- #endif
- t.continuation_root = SCM_EOL;
- t.continuation_base = t.base;
- scm_i_pthread_cond_init (&t.sleep_cond, NULL);
- t.sleep_mutex = NULL;
- t.sleep_object = SCM_BOOL_F;
- t.sleep_fd = -1;
- t.vp = NULL;
- if (pipe2 (t.sleep_pipe, O_CLOEXEC) != 0)
- /* FIXME: Error conditions during the initialization phase are handled
- gracelessly since public functions such as `scm_init_guile ()'
- currently have type `void'. */
- abort ();
- scm_i_pthread_mutex_init (&t.admin_mutex, NULL);
- t.canceled = 0;
- t.exited = 0;
- t.guile_mode = 0;
- /* The switcheroo. */
- {
- scm_i_thread *t_ptr = &t;
-
- GC_disable ();
- t_ptr = GC_generic_malloc (sizeof (*t_ptr), thread_gc_kind);
- memcpy (t_ptr, &t, sizeof t);
- scm_i_pthread_setspecific (scm_i_thread_key, t_ptr);
- #ifdef SCM_HAVE_THREAD_STORAGE_CLASS
- /* Cache the current thread in TLS for faster lookup. */
- scm_i_current_thread = t_ptr;
- #endif
- scm_i_pthread_mutex_lock (&thread_admin_mutex);
- t_ptr->next_thread = all_threads;
- all_threads = t_ptr;
- thread_count++;
- scm_i_pthread_mutex_unlock (&thread_admin_mutex);
- GC_enable ();
- }
- }
- /* Perform second stage of thread initialisation, in guile mode.
- */
- static void
- guilify_self_2 (SCM parent)
- {
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- t->guile_mode = 1;
- SCM_NEWSMOB (t->handle, scm_tc16_thread, t);
- t->continuation_root = scm_cons (t->handle, SCM_EOL);
- t->continuation_base = t->base;
- {
- size_t size = SCM_INLINE_GC_FREELIST_COUNT * sizeof (void *);
- t->freelists = scm_gc_malloc (size, "freelists");
- t->pointerless_freelists = scm_gc_malloc (size, "atomic freelists");
- }
- if (scm_is_true (parent))
- t->dynamic_state = scm_make_dynamic_state (parent);
- else
- t->dynamic_state = scm_i_make_initial_dynamic_state ();
- t->dynstack.base = scm_gc_malloc (16 * sizeof (scm_t_bits), "dynstack");
- t->dynstack.limit = t->dynstack.base + 16;
- t->dynstack.top = t->dynstack.base + SCM_DYNSTACK_HEADER_LEN;
- t->join_queue = make_queue ();
- t->block_asyncs = 0;
- /* See note in finalizers.c:queue_finalizer_async(). */
- GC_invoke_finalizers ();
- }
- /*** Fat mutexes */
- /* We implement our own mutex type since we want them to be 'fair', we
- want to do fancy things while waiting for them (like running
- asyncs) and we might want to add things that are nice for
- debugging.
- */
- typedef struct {
- scm_i_pthread_mutex_t lock;
- SCM owner;
- int level; /* how much the owner owns us. <= 1 for non-recursive mutexes */
- int recursive; /* allow recursive locking? */
- int unchecked_unlock; /* is it an error to unlock an unlocked mutex? */
- int allow_external_unlock; /* is it an error to unlock a mutex that is not
- owned by the current thread? */
- SCM waiting; /* the threads waiting for this mutex. */
- } fat_mutex;
- #define SCM_MUTEXP(x) SCM_SMOB_PREDICATE (scm_tc16_mutex, x)
- #define SCM_MUTEX_DATA(x) ((fat_mutex *) SCM_SMOB_DATA (x))
- static SCM
- call_cleanup (void *data)
- {
- SCM *proc_p = data;
- return scm_call_0 (*proc_p);
- }
-
- /* Perform thread tear-down, in guile mode.
- */
- static void *
- do_thread_exit (void *v)
- {
- scm_i_thread *t = (scm_i_thread *) v;
- if (!scm_is_false (t->cleanup_handler))
- {
- SCM ptr = t->cleanup_handler;
- t->cleanup_handler = SCM_BOOL_F;
- t->result = scm_internal_catch (SCM_BOOL_T,
- call_cleanup, &ptr,
- scm_handle_by_message_noexit, NULL);
- }
- scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
- t->exited = 1;
- close (t->sleep_pipe[0]);
- close (t->sleep_pipe[1]);
- while (scm_is_true (unblock_from_queue (t->join_queue)))
- ;
- while (!scm_is_null (t->mutexes))
- {
- SCM mutex = scm_c_weak_vector_ref (scm_car (t->mutexes), 0);
- if (scm_is_true (mutex))
- {
- fat_mutex *m = SCM_MUTEX_DATA (mutex);
- scm_i_pthread_mutex_lock (&m->lock);
- /* Check whether T owns MUTEX. This is usually the case, unless
- T abandoned MUTEX; in that case, T is no longer its owner (see
- `fat_mutex_lock') but MUTEX is still in `t->mutexes'. */
- if (scm_is_eq (m->owner, t->handle))
- unblock_from_queue (m->waiting);
- scm_i_pthread_mutex_unlock (&m->lock);
- }
- t->mutexes = scm_cdr (t->mutexes);
- }
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- return NULL;
- }
- static void *
- do_thread_exit_trampoline (struct GC_stack_base *sb, void *v)
- {
- /* Won't hurt if we are already registered. */
- #if SCM_USE_PTHREAD_THREADS
- GC_register_my_thread (sb);
- #endif
- return scm_with_guile (do_thread_exit, v);
- }
- static void
- on_thread_exit (void *v)
- {
- /* This handler is executed in non-guile mode. */
- scm_i_thread *t = (scm_i_thread *) v, **tp;
- /* If we were canceled, we were unable to clear `t->guile_mode', so do
- it here. */
- t->guile_mode = 0;
- /* If this thread was cancelled while doing a cond wait, it will
- still have a mutex locked, so we unlock it here. */
- if (t->held_mutex)
- {
- scm_i_pthread_mutex_unlock (t->held_mutex);
- t->held_mutex = NULL;
- }
- /* Reinstate the current thread for purposes of scm_with_guile
- guile-mode cleanup handlers. Only really needed in the non-TLS
- case but it doesn't hurt to be consistent. */
- scm_i_pthread_setspecific (scm_i_thread_key, t);
- /* Scheme-level thread finalizers and other cleanup needs to happen in
- guile mode. */
- GC_call_with_stack_base (do_thread_exit_trampoline, t);
- /* Removing ourself from the list of all threads needs to happen in
- non-guile mode since all SCM values on our stack become
- unprotected once we are no longer in the list. */
- scm_i_pthread_mutex_lock (&thread_admin_mutex);
- for (tp = &all_threads; *tp; tp = &(*tp)->next_thread)
- if (*tp == t)
- {
- *tp = t->next_thread;
- /* GC-robust */
- t->next_thread = NULL;
- break;
- }
- thread_count--;
- /* If there's only one other thread, it could be the signal delivery
- thread, so we need to notify it to shut down by closing its read pipe.
- If it's not the signal delivery thread, then closing the read pipe isn't
- going to hurt. */
- if (thread_count <= 1)
- scm_i_close_signal_pipe ();
- scm_i_pthread_mutex_unlock (&thread_admin_mutex);
- scm_i_pthread_setspecific (scm_i_thread_key, NULL);
- if (t->vp)
- {
- scm_i_vm_free_stack (t->vp);
- t->vp = NULL;
- }
- #if SCM_USE_PTHREAD_THREADS
- GC_unregister_my_thread ();
- #endif
- }
- static scm_i_pthread_once_t init_thread_key_once = SCM_I_PTHREAD_ONCE_INIT;
- static void
- init_thread_key (void)
- {
- scm_i_pthread_key_create (&scm_i_thread_key, on_thread_exit);
- }
- /* Perform any initializations necessary to make the current thread
- known to Guile (via SCM_I_CURRENT_THREAD), initializing Guile itself,
- if necessary.
- BASE is the stack base to use with GC.
- PARENT is the dynamic state to use as the parent, ot SCM_BOOL_F in
- which case the default dynamic state is used.
- Returns zero when the thread was known to guile already; otherwise
- return 1.
- Note that it could be the case that the thread was known
- to Guile, but not in guile mode (because we are within a
- scm_without_guile call). Check SCM_I_CURRENT_THREAD->guile_mode to
- be sure. New threads are put into guile mode implicitly. */
- static int
- scm_i_init_thread_for_guile (struct GC_stack_base *base, SCM parent)
- {
- scm_i_pthread_once (&init_thread_key_once, init_thread_key);
- if (SCM_I_CURRENT_THREAD)
- {
- /* Thread is already known to Guile.
- */
- return 0;
- }
- else
- {
- /* This thread has not been guilified yet.
- */
- scm_i_pthread_mutex_lock (&scm_i_init_mutex);
- if (scm_initialized_p == 0)
- {
- /* First thread ever to enter Guile. Run the full
- initialization.
- */
- scm_i_init_guile (base);
- #if SCM_USE_PTHREAD_THREADS
- /* Allow other threads to come in later. */
- GC_allow_register_threads ();
- #endif
- scm_i_pthread_mutex_unlock (&scm_i_init_mutex);
- }
- else
- {
- /* Guile is already initialized, but this thread enters it for
- the first time. Only initialize this thread.
- */
- scm_i_pthread_mutex_unlock (&scm_i_init_mutex);
- /* Register this thread with libgc. */
- #if SCM_USE_PTHREAD_THREADS
- GC_register_my_thread (base);
- #endif
- guilify_self_1 (base);
- guilify_self_2 (parent);
- }
- return 1;
- }
- }
- void
- scm_init_guile ()
- {
- struct GC_stack_base stack_base;
-
- if (GC_get_stack_base (&stack_base) == GC_SUCCESS)
- scm_i_init_thread_for_guile (&stack_base,
- scm_i_default_dynamic_state);
- else
- {
- fprintf (stderr, "Failed to get stack base for current thread.\n");
- exit (EXIT_FAILURE);
- }
- }
- struct with_guile_args
- {
- GC_fn_type func;
- void *data;
- SCM parent;
- };
- static void *
- with_guile_trampoline (void *data)
- {
- struct with_guile_args *args = data;
- return scm_c_with_continuation_barrier (args->func, args->data);
- }
-
- static void *
- with_guile_and_parent (struct GC_stack_base *base, void *data)
- {
- void *res;
- int new_thread;
- scm_i_thread *t;
- struct with_guile_args *args = data;
- new_thread = scm_i_init_thread_for_guile (base, args->parent);
- t = SCM_I_CURRENT_THREAD;
- if (new_thread)
- {
- /* We are in Guile mode. */
- assert (t->guile_mode);
- res = scm_c_with_continuation_barrier (args->func, args->data);
- /* Leave Guile mode. */
- t->guile_mode = 0;
- }
- else if (t->guile_mode)
- {
- /* Already in Guile mode. */
- res = scm_c_with_continuation_barrier (args->func, args->data);
- }
- else
- {
- /* We are not in Guile mode, either because we are not within a
- scm_with_guile, or because we are within a scm_without_guile.
- This call to scm_with_guile() could happen from anywhere on the
- stack, and in particular lower on the stack than when it was
- when this thread was first guilified. Thus, `base' must be
- updated. */
- #if SCM_STACK_GROWS_UP
- if (SCM_STACK_PTR (base->mem_base) < t->base)
- t->base = SCM_STACK_PTR (base->mem_base);
- #else
- if (SCM_STACK_PTR (base->mem_base) > t->base)
- t->base = SCM_STACK_PTR (base->mem_base);
- #endif
- t->guile_mode = 1;
- res = GC_call_with_gc_active (with_guile_trampoline, args);
- t->guile_mode = 0;
- }
- return res;
- }
- static void *
- scm_i_with_guile_and_parent (void *(*func)(void *), void *data, SCM parent)
- {
- struct with_guile_args args;
- args.func = func;
- args.data = data;
- args.parent = parent;
-
- return GC_call_with_stack_base (with_guile_and_parent, &args);
- }
- void *
- scm_with_guile (void *(*func)(void *), void *data)
- {
- return scm_i_with_guile_and_parent (func, data,
- scm_i_default_dynamic_state);
- }
- void *
- scm_without_guile (void *(*func)(void *), void *data)
- {
- void *result;
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- if (t->guile_mode)
- {
- SCM_I_CURRENT_THREAD->guile_mode = 0;
- result = GC_do_blocking (func, data);
- SCM_I_CURRENT_THREAD->guile_mode = 1;
- }
- else
- /* Otherwise we're not in guile mode, so nothing to do. */
- result = func (data);
- return result;
- }
- /*** Thread creation */
- typedef struct {
- SCM parent;
- SCM thunk;
- SCM handler;
- SCM thread;
- scm_i_pthread_mutex_t mutex;
- scm_i_pthread_cond_t cond;
- } launch_data;
- static void *
- really_launch (void *d)
- {
- launch_data *data = (launch_data *)d;
- SCM thunk = data->thunk, handler = data->handler;
- scm_i_thread *t;
- t = SCM_I_CURRENT_THREAD;
- scm_i_scm_pthread_mutex_lock (&data->mutex);
- data->thread = scm_current_thread ();
- scm_i_pthread_cond_signal (&data->cond);
- scm_i_pthread_mutex_unlock (&data->mutex);
- if (SCM_UNBNDP (handler))
- t->result = scm_call_0 (thunk);
- else
- t->result = scm_catch (SCM_BOOL_T, thunk, handler);
- return 0;
- }
- static void *
- launch_thread (void *d)
- {
- launch_data *data = (launch_data *)d;
- scm_i_pthread_detach (scm_i_pthread_self ());
- scm_i_with_guile_and_parent (really_launch, d, data->parent);
- return NULL;
- }
- SCM_DEFINE (scm_call_with_new_thread, "call-with-new-thread", 1, 1, 0,
- (SCM thunk, SCM handler),
- "Call @code{thunk} in a new thread and with a new dynamic state,\n"
- "returning a new thread object representing the thread. The procedure\n"
- "@var{thunk} is called via @code{with-continuation-barrier}.\n"
- "\n"
- "When @var{handler} is specified, then @var{thunk} is called from\n"
- "within a @code{catch} with tag @code{#t} that has @var{handler} as its\n"
- "handler. This catch is established inside the continuation barrier.\n"
- "\n"
- "Once @var{thunk} or @var{handler} returns, the return value is made\n"
- "the @emph{exit value} of the thread and the thread is terminated.")
- #define FUNC_NAME s_scm_call_with_new_thread
- {
- launch_data data;
- scm_i_pthread_t id;
- int err;
- SCM_ASSERT (scm_is_true (scm_thunk_p (thunk)), thunk, SCM_ARG1, FUNC_NAME);
- SCM_ASSERT (SCM_UNBNDP (handler) || scm_is_true (scm_procedure_p (handler)),
- handler, SCM_ARG2, FUNC_NAME);
- GC_collect_a_little ();
- data.parent = scm_current_dynamic_state ();
- data.thunk = thunk;
- data.handler = handler;
- data.thread = SCM_BOOL_F;
- scm_i_pthread_mutex_init (&data.mutex, NULL);
- scm_i_pthread_cond_init (&data.cond, NULL);
- scm_i_scm_pthread_mutex_lock (&data.mutex);
- err = scm_i_pthread_create (&id, NULL, launch_thread, &data);
- if (err)
- {
- scm_i_pthread_mutex_unlock (&data.mutex);
- errno = err;
- scm_syserror (NULL);
- }
- while (scm_is_false (data.thread))
- scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
- scm_i_pthread_mutex_unlock (&data.mutex);
- return data.thread;
- }
- #undef FUNC_NAME
- typedef struct {
- SCM parent;
- scm_t_catch_body body;
- void *body_data;
- scm_t_catch_handler handler;
- void *handler_data;
- SCM thread;
- scm_i_pthread_mutex_t mutex;
- scm_i_pthread_cond_t cond;
- } spawn_data;
- static void *
- really_spawn (void *d)
- {
- spawn_data *data = (spawn_data *)d;
- scm_t_catch_body body = data->body;
- void *body_data = data->body_data;
- scm_t_catch_handler handler = data->handler;
- void *handler_data = data->handler_data;
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- scm_i_scm_pthread_mutex_lock (&data->mutex);
- data->thread = scm_current_thread ();
- scm_i_pthread_cond_signal (&data->cond);
- scm_i_pthread_mutex_unlock (&data->mutex);
- if (handler == NULL)
- t->result = body (body_data);
- else
- t->result = scm_internal_catch (SCM_BOOL_T,
- body, body_data,
- handler, handler_data);
- return 0;
- }
- static void *
- spawn_thread (void *d)
- {
- spawn_data *data = (spawn_data *)d;
- scm_i_pthread_detach (scm_i_pthread_self ());
- scm_i_with_guile_and_parent (really_spawn, d, data->parent);
- return NULL;
- }
- SCM
- scm_spawn_thread (scm_t_catch_body body, void *body_data,
- scm_t_catch_handler handler, void *handler_data)
- {
- spawn_data data;
- scm_i_pthread_t id;
- int err;
- data.parent = scm_current_dynamic_state ();
- data.body = body;
- data.body_data = body_data;
- data.handler = handler;
- data.handler_data = handler_data;
- data.thread = SCM_BOOL_F;
- scm_i_pthread_mutex_init (&data.mutex, NULL);
- scm_i_pthread_cond_init (&data.cond, NULL);
- scm_i_scm_pthread_mutex_lock (&data.mutex);
- err = scm_i_pthread_create (&id, NULL, spawn_thread, &data);
- if (err)
- {
- scm_i_pthread_mutex_unlock (&data.mutex);
- errno = err;
- scm_syserror (NULL);
- }
- while (scm_is_false (data.thread))
- scm_i_scm_pthread_cond_wait (&data.cond, &data.mutex);
- scm_i_pthread_mutex_unlock (&data.mutex);
- assert (SCM_I_IS_THREAD (data.thread));
- return data.thread;
- }
- SCM_DEFINE (scm_yield, "yield", 0, 0, 0,
- (),
- "Move the calling thread to the end of the scheduling queue.")
- #define FUNC_NAME s_scm_yield
- {
- return scm_from_bool (scm_i_sched_yield ());
- }
- #undef FUNC_NAME
- /* Some systems, notably Android, lack 'pthread_cancel'. Don't provide
- 'cancel-thread' on these systems. */
- #if !SCM_USE_PTHREAD_THREADS || defined HAVE_PTHREAD_CANCEL
- SCM_DEFINE (scm_cancel_thread, "cancel-thread", 1, 0, 0,
- (SCM thread),
- "Asynchronously force the target @var{thread} to terminate. @var{thread} "
- "cannot be the current thread, and if @var{thread} has already terminated or "
- "been signaled to terminate, this function is a no-op.")
- #define FUNC_NAME s_scm_cancel_thread
- {
- scm_i_thread *t = NULL;
- SCM_VALIDATE_THREAD (1, thread);
- t = SCM_I_THREAD_DATA (thread);
- scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
- if (!t->canceled)
- {
- t->canceled = 1;
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- scm_i_pthread_cancel (t->pthread);
- }
- else
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- return SCM_UNSPECIFIED;
- }
- #undef FUNC_NAME
- #endif
- SCM_DEFINE (scm_set_thread_cleanup_x, "set-thread-cleanup!", 2, 0, 0,
- (SCM thread, SCM proc),
- "Set the thunk @var{proc} as the cleanup handler for the thread @var{thread}. "
- "This handler will be called when the thread exits.")
- #define FUNC_NAME s_scm_set_thread_cleanup_x
- {
- scm_i_thread *t;
- SCM_VALIDATE_THREAD (1, thread);
- if (!scm_is_false (proc))
- SCM_VALIDATE_THUNK (2, proc);
- t = SCM_I_THREAD_DATA (thread);
- scm_i_pthread_mutex_lock (&t->admin_mutex);
- if (!(t->exited || t->canceled))
- t->cleanup_handler = proc;
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- return SCM_UNSPECIFIED;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_thread_cleanup, "thread-cleanup", 1, 0, 0,
- (SCM thread),
- "Return the cleanup handler installed for the thread @var{thread}.")
- #define FUNC_NAME s_scm_thread_cleanup
- {
- scm_i_thread *t;
- SCM ret;
- SCM_VALIDATE_THREAD (1, thread);
- t = SCM_I_THREAD_DATA (thread);
- scm_i_pthread_mutex_lock (&t->admin_mutex);
- ret = (t->exited || t->canceled) ? SCM_BOOL_F : t->cleanup_handler;
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- return ret;
- }
- #undef FUNC_NAME
- SCM scm_join_thread (SCM thread)
- {
- return scm_join_thread_timed (thread, SCM_UNDEFINED, SCM_UNDEFINED);
- }
- SCM_DEFINE (scm_join_thread_timed, "join-thread", 1, 2, 0,
- (SCM thread, SCM timeout, SCM timeoutval),
- "Suspend execution of the calling thread until the target @var{thread} "
- "terminates, unless the target @var{thread} has already terminated. ")
- #define FUNC_NAME s_scm_join_thread_timed
- {
- scm_i_thread *t;
- scm_t_timespec ctimeout, *timeout_ptr = NULL;
- SCM res = SCM_BOOL_F;
- if (! (SCM_UNBNDP (timeoutval)))
- res = timeoutval;
- SCM_VALIDATE_THREAD (1, thread);
- if (scm_is_eq (scm_current_thread (), thread))
- SCM_MISC_ERROR ("cannot join the current thread", SCM_EOL);
- t = SCM_I_THREAD_DATA (thread);
- scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
- if (! SCM_UNBNDP (timeout))
- {
- to_timespec (timeout, &ctimeout);
- timeout_ptr = &ctimeout;
- }
- if (t->exited)
- res = t->result;
- else
- {
- while (1)
- {
- int err = block_self (t->join_queue, thread, &t->admin_mutex,
- timeout_ptr);
- if (err == 0)
- {
- if (t->exited)
- {
- res = t->result;
- break;
- }
- }
- else if (err == ETIMEDOUT)
- break;
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- SCM_TICK;
- scm_i_scm_pthread_mutex_lock (&t->admin_mutex);
- /* Check for exit again, since we just released and
- reacquired the admin mutex, before the next block_self
- call (which would block forever if t has already
- exited). */
- if (t->exited)
- {
- res = t->result;
- break;
- }
- }
- }
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- return res;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_thread_p, "thread?", 1, 0, 0,
- (SCM obj),
- "Return @code{#t} if @var{obj} is a thread.")
- #define FUNC_NAME s_scm_thread_p
- {
- return SCM_I_IS_THREAD(obj) ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- static int
- fat_mutex_print (SCM mx, SCM port, scm_print_state *pstate SCM_UNUSED)
- {
- fat_mutex *m = SCM_MUTEX_DATA (mx);
- scm_puts_unlocked ("#<mutex ", port);
- scm_uintprint ((scm_t_bits)m, 16, port);
- scm_puts_unlocked (">", port);
- return 1;
- }
- static SCM
- make_fat_mutex (int recursive, int unchecked_unlock, int external_unlock)
- {
- fat_mutex *m;
- SCM mx;
- scm_i_pthread_mutex_t lock = SCM_I_PTHREAD_MUTEX_INITIALIZER;
- m = scm_gc_malloc (sizeof (fat_mutex), "mutex");
- /* Because PTHREAD_MUTEX_INITIALIZER is static, it's plain old data,
- and so we can just copy it. */
- memcpy (&m->lock, &lock, sizeof (m->lock));
- m->owner = SCM_BOOL_F;
- m->level = 0;
- m->recursive = recursive;
- m->unchecked_unlock = unchecked_unlock;
- m->allow_external_unlock = external_unlock;
- m->waiting = SCM_EOL;
- SCM_NEWSMOB (mx, scm_tc16_mutex, (scm_t_bits) m);
- m->waiting = make_queue ();
- return mx;
- }
- SCM scm_make_mutex (void)
- {
- return scm_make_mutex_with_flags (SCM_EOL);
- }
- SCM_SYMBOL (unchecked_unlock_sym, "unchecked-unlock");
- SCM_SYMBOL (allow_external_unlock_sym, "allow-external-unlock");
- SCM_SYMBOL (recursive_sym, "recursive");
- SCM_DEFINE (scm_make_mutex_with_flags, "make-mutex", 0, 0, 1,
- (SCM flags),
- "Create a new mutex. ")
- #define FUNC_NAME s_scm_make_mutex_with_flags
- {
- int unchecked_unlock = 0, external_unlock = 0, recursive = 0;
- SCM ptr = flags;
- while (! scm_is_null (ptr))
- {
- SCM flag = SCM_CAR (ptr);
- if (scm_is_eq (flag, unchecked_unlock_sym))
- unchecked_unlock = 1;
- else if (scm_is_eq (flag, allow_external_unlock_sym))
- external_unlock = 1;
- else if (scm_is_eq (flag, recursive_sym))
- recursive = 1;
- else
- SCM_MISC_ERROR ("unsupported mutex option: ~a", scm_list_1 (flag));
- ptr = SCM_CDR (ptr);
- }
- return make_fat_mutex (recursive, unchecked_unlock, external_unlock);
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_make_recursive_mutex, "make-recursive-mutex", 0, 0, 0,
- (void),
- "Create a new recursive mutex. ")
- #define FUNC_NAME s_scm_make_recursive_mutex
- {
- return make_fat_mutex (1, 0, 0);
- }
- #undef FUNC_NAME
- SCM_SYMBOL (scm_abandoned_mutex_error_key, "abandoned-mutex-error");
- static SCM
- fat_mutex_lock (SCM mutex, scm_t_timespec *timeout, SCM owner, int *ret)
- {
- fat_mutex *m = SCM_MUTEX_DATA (mutex);
- SCM new_owner = SCM_UNBNDP (owner) ? scm_current_thread() : owner;
- SCM err = SCM_BOOL_F;
- struct timeval current_time;
- scm_i_scm_pthread_mutex_lock (&m->lock);
- while (1)
- {
- if (m->level == 0)
- {
- m->owner = new_owner;
- m->level++;
- if (SCM_I_IS_THREAD (new_owner))
- {
- scm_i_thread *t = SCM_I_THREAD_DATA (new_owner);
- /* FIXME: The order in which `t->admin_mutex' and
- `m->lock' are taken differs from that in
- `on_thread_exit', potentially leading to deadlocks. */
- scm_i_pthread_mutex_lock (&t->admin_mutex);
- /* Only keep a weak reference to MUTEX so that it's not
- retained when not referenced elsewhere (bug #27450).
- The weak pair itself is eventually removed when MUTEX
- is unlocked. Note that `t->mutexes' lists mutexes
- currently held by T, so it should be small. */
- t->mutexes = scm_cons (scm_make_weak_vector (SCM_INUM1, mutex),
- t->mutexes);
- scm_i_pthread_mutex_unlock (&t->admin_mutex);
- }
- *ret = 1;
- break;
- }
- else if (SCM_I_IS_THREAD (m->owner) && scm_c_thread_exited_p (m->owner))
- {
- m->owner = new_owner;
- err = scm_cons (scm_abandoned_mutex_error_key,
- scm_from_locale_string ("lock obtained on abandoned "
- "mutex"));
- *ret = 1;
- break;
- }
- else if (scm_is_eq (m->owner, new_owner))
- {
- if (m->recursive)
- {
- m->level++;
- *ret = 1;
- }
- else
- {
- err = scm_cons (scm_misc_error_key,
- scm_from_locale_string ("mutex already locked "
- "by thread"));
- *ret = 0;
- }
- break;
- }
- else
- {
- if (timeout != NULL)
- {
- gettimeofday (¤t_time, NULL);
- if (current_time.tv_sec > timeout->tv_sec ||
- (current_time.tv_sec == timeout->tv_sec &&
- current_time.tv_usec * 1000 > timeout->tv_nsec))
- {
- *ret = 0;
- break;
- }
- }
- block_self (m->waiting, mutex, &m->lock, timeout);
- scm_i_pthread_mutex_unlock (&m->lock);
- SCM_TICK;
- scm_i_scm_pthread_mutex_lock (&m->lock);
- }
- }
- scm_i_pthread_mutex_unlock (&m->lock);
- return err;
- }
- SCM scm_lock_mutex (SCM mx)
- {
- return scm_lock_mutex_timed (mx, SCM_UNDEFINED, SCM_UNDEFINED);
- }
- SCM_DEFINE (scm_lock_mutex_timed, "lock-mutex", 1, 2, 0,
- (SCM m, SCM timeout, SCM owner),
- "Lock mutex @var{m}. If the mutex is already locked, the calling\n"
- "thread blocks until the mutex becomes available. The function\n"
- "returns when the calling thread owns the lock on @var{m}.\n"
- "Locking a mutex that a thread already owns will succeed right\n"
- "away and will not block the thread. That is, Guile's mutexes\n"
- "are @emph{recursive}.")
- #define FUNC_NAME s_scm_lock_mutex_timed
- {
- SCM exception;
- int ret = 0;
- scm_t_timespec cwaittime, *waittime = NULL;
- SCM_VALIDATE_MUTEX (1, m);
- if (! SCM_UNBNDP (timeout) && ! scm_is_false (timeout))
- {
- to_timespec (timeout, &cwaittime);
- waittime = &cwaittime;
- }
- if (!SCM_UNBNDP (owner) && !scm_is_false (owner))
- SCM_VALIDATE_THREAD (3, owner);
- exception = fat_mutex_lock (m, waittime, owner, &ret);
- if (!scm_is_false (exception))
- scm_ithrow (SCM_CAR (exception), scm_list_1 (SCM_CDR (exception)), 1);
- return ret ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- static void
- lock_mutex_return_void (SCM mx)
- {
- (void) scm_lock_mutex (mx);
- }
- static void
- unlock_mutex_return_void (SCM mx)
- {
- (void) scm_unlock_mutex (mx);
- }
- void
- scm_dynwind_lock_mutex (SCM mutex)
- {
- scm_dynwind_unwind_handler_with_scm (unlock_mutex_return_void, mutex,
- SCM_F_WIND_EXPLICITLY);
- scm_dynwind_rewind_handler_with_scm (lock_mutex_return_void, mutex,
- SCM_F_WIND_EXPLICITLY);
- }
- SCM_DEFINE (scm_try_mutex, "try-mutex", 1, 0, 0,
- (SCM mutex),
- "Try to lock @var{mutex}. If the mutex is already locked by someone "
- "else, return @code{#f}. Else lock the mutex and return @code{#t}. ")
- #define FUNC_NAME s_scm_try_mutex
- {
- SCM exception;
- int ret = 0;
- scm_t_timespec cwaittime, *waittime = NULL;
- SCM_VALIDATE_MUTEX (1, mutex);
- to_timespec (scm_from_int(0), &cwaittime);
- waittime = &cwaittime;
- exception = fat_mutex_lock (mutex, waittime, SCM_UNDEFINED, &ret);
- if (!scm_is_false (exception))
- scm_ithrow (SCM_CAR (exception), scm_list_1 (SCM_CDR (exception)), 1);
- return ret ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- /*** Fat condition variables */
- typedef struct {
- scm_i_pthread_mutex_t lock;
- SCM waiting; /* the threads waiting for this condition. */
- } fat_cond;
- #define SCM_CONDVARP(x) SCM_SMOB_PREDICATE (scm_tc16_condvar, x)
- #define SCM_CONDVAR_DATA(x) ((fat_cond *) SCM_SMOB_DATA (x))
- static void
- remove_mutex_from_thread (SCM mutex, scm_i_thread *t)
- {
- SCM walk, prev;
-
- for (prev = SCM_BOOL_F, walk = t->mutexes; scm_is_pair (walk);
- walk = SCM_CDR (walk))
- {
- if (scm_is_eq (mutex, scm_c_weak_vector_ref (SCM_CAR (walk), 0)))
- {
- if (scm_is_pair (prev))
- SCM_SETCDR (prev, SCM_CDR (walk));
- else
- t->mutexes = SCM_CDR (walk);
- break;
- }
- }
- }
- static int
- fat_mutex_unlock (SCM mutex, SCM cond,
- const scm_t_timespec *waittime, int relock)
- {
- SCM owner;
- fat_mutex *m = SCM_MUTEX_DATA (mutex);
- fat_cond *c = NULL;
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- int err = 0, ret = 0;
- scm_i_scm_pthread_mutex_lock (&m->lock);
- owner = m->owner;
- if (!scm_is_eq (owner, t->handle))
- {
- if (m->level == 0)
- {
- if (!m->unchecked_unlock)
- {
- scm_i_pthread_mutex_unlock (&m->lock);
- scm_misc_error (NULL, "mutex not locked", SCM_EOL);
- }
- owner = t->handle;
- }
- else if (!m->allow_external_unlock)
- {
- scm_i_pthread_mutex_unlock (&m->lock);
- scm_misc_error (NULL, "mutex not locked by current thread", SCM_EOL);
- }
- }
- if (! (SCM_UNBNDP (cond)))
- {
- c = SCM_CONDVAR_DATA (cond);
- while (1)
- {
- int brk = 0;
- if (m->level > 0)
- m->level--;
- if (m->level == 0)
- {
- /* Change the owner of MUTEX. */
- remove_mutex_from_thread (mutex, t);
- m->owner = unblock_from_queue (m->waiting);
- }
- t->block_asyncs++;
- err = block_self (c->waiting, cond, &m->lock, waittime);
- scm_i_pthread_mutex_unlock (&m->lock);
- if (err == 0)
- {
- ret = 1;
- brk = 1;
- }
- else if (err == ETIMEDOUT)
- {
- ret = 0;
- brk = 1;
- }
- else if (err != EINTR)
- {
- errno = err;
- scm_syserror (NULL);
- }
- if (brk)
- {
- if (relock)
- scm_lock_mutex_timed (mutex, SCM_UNDEFINED, owner);
- t->block_asyncs--;
- break;
- }
- t->block_asyncs--;
- scm_async_tick ();
- scm_remember_upto_here_2 (cond, mutex);
- scm_i_scm_pthread_mutex_lock (&m->lock);
- }
- }
- else
- {
- if (m->level > 0)
- m->level--;
- if (m->level == 0)
- {
- /* Change the owner of MUTEX. */
- remove_mutex_from_thread (mutex, t);
- m->owner = unblock_from_queue (m->waiting);
- }
- scm_i_pthread_mutex_unlock (&m->lock);
- ret = 1;
- }
- return ret;
- }
- SCM scm_unlock_mutex (SCM mx)
- {
- return scm_unlock_mutex_timed (mx, SCM_UNDEFINED, SCM_UNDEFINED);
- }
- SCM_DEFINE (scm_unlock_mutex_timed, "unlock-mutex", 1, 2, 0,
- (SCM mx, SCM cond, SCM timeout),
- "Unlocks @var{mutex} if the calling thread owns the lock on "
- "@var{mutex}. Calling unlock-mutex on a mutex not owned by the current "
- "thread results in undefined behaviour. Once a mutex has been unlocked, "
- "one thread blocked on @var{mutex} is awakened and grabs the mutex "
- "lock. Every call to @code{lock-mutex} by this thread must be matched "
- "with a call to @code{unlock-mutex}. Only the last call to "
- "@code{unlock-mutex} will actually unlock the mutex. ")
- #define FUNC_NAME s_scm_unlock_mutex_timed
- {
- scm_t_timespec cwaittime, *waittime = NULL;
- SCM_VALIDATE_MUTEX (1, mx);
- if (! (SCM_UNBNDP (cond)))
- {
- SCM_VALIDATE_CONDVAR (2, cond);
- if (! SCM_UNBNDP (timeout) && ! scm_is_false (timeout))
- {
- to_timespec (timeout, &cwaittime);
- waittime = &cwaittime;
- }
- }
- return fat_mutex_unlock (mx, cond, waittime, 0) ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_mutex_p, "mutex?", 1, 0, 0,
- (SCM obj),
- "Return @code{#t} if @var{obj} is a mutex.")
- #define FUNC_NAME s_scm_mutex_p
- {
- return SCM_MUTEXP (obj) ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_mutex_owner, "mutex-owner", 1, 0, 0,
- (SCM mx),
- "Return the thread owning @var{mx}, or @code{#f}.")
- #define FUNC_NAME s_scm_mutex_owner
- {
- SCM owner;
- fat_mutex *m = NULL;
- SCM_VALIDATE_MUTEX (1, mx);
- m = SCM_MUTEX_DATA (mx);
- scm_i_pthread_mutex_lock (&m->lock);
- owner = m->owner;
- scm_i_pthread_mutex_unlock (&m->lock);
- return owner;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_mutex_level, "mutex-level", 1, 0, 0,
- (SCM mx),
- "Return the lock level of mutex @var{mx}.")
- #define FUNC_NAME s_scm_mutex_level
- {
- SCM_VALIDATE_MUTEX (1, mx);
- return scm_from_int (SCM_MUTEX_DATA(mx)->level);
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_mutex_locked_p, "mutex-locked?", 1, 0, 0,
- (SCM mx),
- "Returns @code{#t} if the mutex @var{mx} is locked.")
- #define FUNC_NAME s_scm_mutex_locked_p
- {
- SCM_VALIDATE_MUTEX (1, mx);
- return SCM_MUTEX_DATA (mx)->level > 0 ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- static int
- fat_cond_print (SCM cv, SCM port, scm_print_state *pstate SCM_UNUSED)
- {
- fat_cond *c = SCM_CONDVAR_DATA (cv);
- scm_puts_unlocked ("#<condition-variable ", port);
- scm_uintprint ((scm_t_bits)c, 16, port);
- scm_puts_unlocked (">", port);
- return 1;
- }
- SCM_DEFINE (scm_make_condition_variable, "make-condition-variable", 0, 0, 0,
- (void),
- "Make a new condition variable.")
- #define FUNC_NAME s_scm_make_condition_variable
- {
- fat_cond *c;
- SCM cv;
- c = scm_gc_malloc (sizeof (fat_cond), "condition variable");
- c->waiting = SCM_EOL;
- SCM_NEWSMOB (cv, scm_tc16_condvar, (scm_t_bits) c);
- c->waiting = make_queue ();
- return cv;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_timed_wait_condition_variable, "wait-condition-variable", 2, 1, 0,
- (SCM cv, SCM mx, SCM t),
- "Wait until condition variable @var{cv} has been signalled. While waiting, "
- "mutex @var{mx} is atomically unlocked (as with @code{unlock-mutex}) and "
- "is locked again when this function returns. When @var{t} is given, "
- "it specifies a point in time where the waiting should be aborted. It "
- "can be either a integer as returned by @code{current-time} or a pair "
- "as returned by @code{gettimeofday}. When the waiting is aborted the "
- "mutex is locked and @code{#f} is returned. When the condition "
- "variable is in fact signalled, the mutex is also locked and @code{#t} "
- "is returned. ")
- #define FUNC_NAME s_scm_timed_wait_condition_variable
- {
- scm_t_timespec waittime, *waitptr = NULL;
- SCM_VALIDATE_CONDVAR (1, cv);
- SCM_VALIDATE_MUTEX (2, mx);
- if (!SCM_UNBNDP (t))
- {
- to_timespec (t, &waittime);
- waitptr = &waittime;
- }
- return fat_mutex_unlock (mx, cv, waitptr, 1) ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- static void
- fat_cond_signal (fat_cond *c)
- {
- unblock_from_queue (c->waiting);
- }
- SCM_DEFINE (scm_signal_condition_variable, "signal-condition-variable", 1, 0, 0,
- (SCM cv),
- "Wake up one thread that is waiting for @var{cv}")
- #define FUNC_NAME s_scm_signal_condition_variable
- {
- SCM_VALIDATE_CONDVAR (1, cv);
- fat_cond_signal (SCM_CONDVAR_DATA (cv));
- return SCM_BOOL_T;
- }
- #undef FUNC_NAME
- static void
- fat_cond_broadcast (fat_cond *c)
- {
- while (scm_is_true (unblock_from_queue (c->waiting)))
- ;
- }
- SCM_DEFINE (scm_broadcast_condition_variable, "broadcast-condition-variable", 1, 0, 0,
- (SCM cv),
- "Wake up all threads that are waiting for @var{cv}. ")
- #define FUNC_NAME s_scm_broadcast_condition_variable
- {
- SCM_VALIDATE_CONDVAR (1, cv);
- fat_cond_broadcast (SCM_CONDVAR_DATA (cv));
- return SCM_BOOL_T;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_condition_variable_p, "condition-variable?", 1, 0, 0,
- (SCM obj),
- "Return @code{#t} if @var{obj} is a condition variable.")
- #define FUNC_NAME s_scm_condition_variable_p
- {
- return SCM_CONDVARP(obj) ? SCM_BOOL_T : SCM_BOOL_F;
- }
- #undef FUNC_NAME
- /*** Select */
- struct select_args
- {
- int nfds;
- fd_set *read_fds;
- fd_set *write_fds;
- fd_set *except_fds;
- struct timeval *timeout;
- int result;
- int errno_value;
- };
- static void *
- do_std_select (void *args)
- {
- struct select_args *select_args;
- select_args = (struct select_args *) args;
- select_args->result =
- select (select_args->nfds,
- select_args->read_fds, select_args->write_fds,
- select_args->except_fds, select_args->timeout);
- select_args->errno_value = errno;
- return NULL;
- }
- int
- scm_std_select (int nfds,
- fd_set *readfds,
- fd_set *writefds,
- fd_set *exceptfds,
- struct timeval *timeout)
- {
- fd_set my_readfds;
- int res, eno, wakeup_fd;
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- struct select_args args;
- if (readfds == NULL)
- {
- FD_ZERO (&my_readfds);
- readfds = &my_readfds;
- }
- while (scm_i_setup_sleep (t, SCM_BOOL_F, NULL, t->sleep_pipe[1]))
- SCM_TICK;
- wakeup_fd = t->sleep_pipe[0];
- FD_SET (wakeup_fd, readfds);
- if (wakeup_fd >= nfds)
- nfds = wakeup_fd+1;
- args.nfds = nfds;
- args.read_fds = readfds;
- args.write_fds = writefds;
- args.except_fds = exceptfds;
- args.timeout = timeout;
- /* Explicitly cooperate with the GC. */
- scm_without_guile (do_std_select, &args);
- res = args.result;
- eno = args.errno_value;
- t->sleep_fd = -1;
- scm_i_reset_sleep (t);
- if (res > 0 && FD_ISSET (wakeup_fd, readfds))
- {
- char dummy;
- full_read (wakeup_fd, &dummy, 1);
- FD_CLR (wakeup_fd, readfds);
- res -= 1;
- if (res == 0)
- {
- eno = EINTR;
- res = -1;
- }
- }
- errno = eno;
- return res;
- }
- /* Convenience API for blocking while in guile mode. */
- #if SCM_USE_PTHREAD_THREADS
- /* It seems reasonable to not run procedures related to mutex and condition
- variables within `GC_do_blocking ()' since, (i) the GC can operate even
- without it, and (ii) the only potential gain would be GC latency. See
- http://thread.gmane.org/gmane.comp.programming.garbage-collection.boehmgc/2245/focus=2251
- for a discussion of the pros and cons. */
- int
- scm_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
- {
- int res = scm_i_pthread_mutex_lock (mutex);
- return res;
- }
- static void
- do_unlock (void *data)
- {
- scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
- }
- void
- scm_dynwind_pthread_mutex_lock (scm_i_pthread_mutex_t *mutex)
- {
- scm_i_scm_pthread_mutex_lock (mutex);
- scm_dynwind_unwind_handler (do_unlock, mutex, SCM_F_WIND_EXPLICITLY);
- }
- int
- scm_pthread_cond_wait (scm_i_pthread_cond_t *cond, scm_i_pthread_mutex_t *mutex)
- {
- int res;
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- t->held_mutex = mutex;
- res = scm_i_pthread_cond_wait (cond, mutex);
- t->held_mutex = NULL;
- return res;
- }
- int
- scm_pthread_cond_timedwait (scm_i_pthread_cond_t *cond,
- scm_i_pthread_mutex_t *mutex,
- const scm_t_timespec *wt)
- {
- int res;
- scm_i_thread *t = SCM_I_CURRENT_THREAD;
- t->held_mutex = mutex;
- res = scm_i_pthread_cond_timedwait (cond, mutex, wt);
- t->held_mutex = NULL;
- return res;
- }
- #endif
- static void
- do_unlock_with_asyncs (void *data)
- {
- scm_i_pthread_mutex_unlock ((scm_i_pthread_mutex_t *)data);
- SCM_I_CURRENT_THREAD->block_asyncs--;
- }
- void
- scm_i_dynwind_pthread_mutex_lock_block_asyncs (scm_i_pthread_mutex_t *mutex)
- {
- SCM_I_CURRENT_THREAD->block_asyncs++;
- scm_i_scm_pthread_mutex_lock (mutex);
- scm_dynwind_unwind_handler (do_unlock_with_asyncs, mutex,
- SCM_F_WIND_EXPLICITLY);
- }
- unsigned long
- scm_std_usleep (unsigned long usecs)
- {
- struct timeval tv;
- tv.tv_usec = usecs % 1000000;
- tv.tv_sec = usecs / 1000000;
- scm_std_select (0, NULL, NULL, NULL, &tv);
- return tv.tv_sec * 1000000 + tv.tv_usec;
- }
- unsigned int
- scm_std_sleep (unsigned int secs)
- {
- struct timeval tv;
- tv.tv_usec = 0;
- tv.tv_sec = secs;
- scm_std_select (0, NULL, NULL, NULL, &tv);
- return tv.tv_sec;
- }
- /*** Misc */
- SCM_DEFINE (scm_current_thread, "current-thread", 0, 0, 0,
- (void),
- "Return the thread that called this function.")
- #define FUNC_NAME s_scm_current_thread
- {
- return SCM_I_CURRENT_THREAD->handle;
- }
- #undef FUNC_NAME
- static SCM
- scm_c_make_list (size_t n, SCM fill)
- {
- SCM res = SCM_EOL;
- while (n-- > 0)
- res = scm_cons (fill, res);
- return res;
- }
- SCM_DEFINE (scm_all_threads, "all-threads", 0, 0, 0,
- (void),
- "Return a list of all threads.")
- #define FUNC_NAME s_scm_all_threads
- {
- /* We can not allocate while holding the thread_admin_mutex because
- of the way GC is done.
- */
- int n = thread_count;
- scm_i_thread *t;
- SCM list = scm_c_make_list (n, SCM_UNSPECIFIED), *l;
- scm_i_pthread_mutex_lock (&thread_admin_mutex);
- l = &list;
- for (t = all_threads; t && n > 0; t = t->next_thread)
- {
- if (t != scm_i_signal_delivery_thread)
- {
- SCM_SETCAR (*l, t->handle);
- l = SCM_CDRLOC (*l);
- }
- n--;
- }
- *l = SCM_EOL;
- scm_i_pthread_mutex_unlock (&thread_admin_mutex);
- return list;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_thread_exited_p, "thread-exited?", 1, 0, 0,
- (SCM thread),
- "Return @code{#t} iff @var{thread} has exited.\n")
- #define FUNC_NAME s_scm_thread_exited_p
- {
- return scm_from_bool (scm_c_thread_exited_p (thread));
- }
- #undef FUNC_NAME
- int
- scm_c_thread_exited_p (SCM thread)
- #define FUNC_NAME s_scm_thread_exited_p
- {
- scm_i_thread *t;
- SCM_VALIDATE_THREAD (1, thread);
- t = SCM_I_THREAD_DATA (thread);
- return t->exited;
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_total_processor_count, "total-processor-count", 0, 0, 0,
- (void),
- "Return the total number of processors of the machine, which\n"
- "is guaranteed to be at least 1. A ``processor'' here is a\n"
- "thread execution unit, which can be either:\n\n"
- "@itemize\n"
- "@item an execution core in a (possibly multi-core) chip, in a\n"
- " (possibly multi- chip) module, in a single computer, or\n"
- "@item a thread execution unit inside a core in the case of\n"
- " @dfn{hyper-threaded} CPUs.\n"
- "@end itemize\n\n"
- "Which of the two definitions is used, is unspecified.\n")
- #define FUNC_NAME s_scm_total_processor_count
- {
- return scm_from_ulong (num_processors (NPROC_ALL));
- }
- #undef FUNC_NAME
- SCM_DEFINE (scm_current_processor_count, "current-processor-count", 0, 0, 0,
- (void),
- "Like @code{total-processor-count}, but return the number of\n"
- "processors available to the current process. See\n"
- "@code{setaffinity} and @code{getaffinity} for more\n"
- "information.\n")
- #define FUNC_NAME s_scm_current_processor_count
- {
- return scm_from_ulong (num_processors (NPROC_CURRENT));
- }
- #undef FUNC_NAME
- static scm_i_pthread_cond_t wake_up_cond;
- static int threads_initialized_p = 0;
- /* This mutex is used by SCM_CRITICAL_SECTION_START/END.
- */
- scm_i_pthread_mutex_t scm_i_critical_section_mutex;
- static SCM dynwind_critical_section_mutex;
- void
- scm_dynwind_critical_section (SCM mutex)
- {
- if (scm_is_false (mutex))
- mutex = dynwind_critical_section_mutex;
- scm_dynwind_lock_mutex (mutex);
- scm_dynwind_block_asyncs ();
- }
- /*** Initialization */
- scm_i_pthread_mutex_t scm_i_misc_mutex;
- #if SCM_USE_PTHREAD_THREADS
- pthread_mutexattr_t scm_i_pthread_mutexattr_recursive[1];
- #endif
- void
- scm_threads_prehistory (void *base)
- {
- #if SCM_USE_PTHREAD_THREADS
- pthread_mutexattr_init (scm_i_pthread_mutexattr_recursive);
- pthread_mutexattr_settype (scm_i_pthread_mutexattr_recursive,
- PTHREAD_MUTEX_RECURSIVE);
- #endif
- scm_i_pthread_mutex_init (&scm_i_critical_section_mutex,
- scm_i_pthread_mutexattr_recursive);
- scm_i_pthread_mutex_init (&scm_i_misc_mutex, NULL);
- scm_i_pthread_cond_init (&wake_up_cond, NULL);
- thread_gc_kind =
- GC_new_kind (GC_new_free_list (),
- GC_MAKE_PROC (GC_new_proc (thread_mark), 0),
- 0, 1);
- guilify_self_1 ((struct GC_stack_base *) base);
- }
- scm_t_bits scm_tc16_thread;
- scm_t_bits scm_tc16_mutex;
- scm_t_bits scm_tc16_condvar;
- void
- scm_init_threads ()
- {
- scm_tc16_thread = scm_make_smob_type ("thread", sizeof (scm_i_thread));
- scm_set_smob_print (scm_tc16_thread, thread_print);
- scm_tc16_mutex = scm_make_smob_type ("mutex", sizeof (fat_mutex));
- scm_set_smob_print (scm_tc16_mutex, fat_mutex_print);
- scm_tc16_condvar = scm_make_smob_type ("condition-variable",
- sizeof (fat_cond));
- scm_set_smob_print (scm_tc16_condvar, fat_cond_print);
- scm_i_default_dynamic_state = SCM_BOOL_F;
- guilify_self_2 (SCM_BOOL_F);
- threads_initialized_p = 1;
- dynwind_critical_section_mutex = scm_make_recursive_mutex ();
- }
- void
- scm_init_threads_default_dynamic_state ()
- {
- SCM state = scm_make_dynamic_state (scm_current_dynamic_state ());
- scm_i_default_dynamic_state = state;
- }
- void
- scm_init_thread_procs ()
- {
- #include "libguile/threads.x"
- }
- /* IA64-specific things. */
- #ifdef __ia64__
- # ifdef __hpux
- # include <sys/param.h>
- # include <sys/pstat.h>
- void *
- scm_ia64_register_backing_store_base (void)
- {
- struct pst_vm_status vm_status;
- int i = 0;
- while (pstat_getprocvm (&vm_status, sizeof (vm_status), 0, i++) == 1)
- if (vm_status.pst_type == PS_RSESTACK)
- return (void *) vm_status.pst_vaddr;
- abort ();
- }
- void *
- scm_ia64_ar_bsp (const void *ctx)
- {
- uint64_t bsp;
- __uc_get_ar_bsp (ctx, &bsp);
- return (void *) bsp;
- }
- # endif /* hpux */
- # ifdef linux
- # include <ucontext.h>
- void *
- scm_ia64_register_backing_store_base (void)
- {
- extern void *__libc_ia64_register_backing_store_base;
- return __libc_ia64_register_backing_store_base;
- }
- void *
- scm_ia64_ar_bsp (const void *opaque)
- {
- const ucontext_t *ctx = opaque;
- return (void *) ctx->uc_mcontext.sc_ar_bsp;
- }
- # endif /* linux */
- # ifdef __FreeBSD__
- # include <ucontext.h>
- void *
- scm_ia64_register_backing_store_base (void)
- {
- return (void *)0x8000000000000000;
- }
- void *
- scm_ia64_ar_bsp (const void *opaque)
- {
- const ucontext_t *ctx = opaque;
- return (void *)(ctx->uc_mcontext.mc_special.bspstore
- + ctx->uc_mcontext.mc_special.ndirty);
- }
- # endif /* __FreeBSD__ */
- #endif /* __ia64__ */
- /*
- Local Variables:
- c-file-style: "gnu"
- End:
- */
|