/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include "lint.h"
#include "thr_uberdata.h"
#include <pthread.h>
#include <procfs.h>
#include <ctype.h>
#include <alloca.h>
#include "libc.h"
/*
* These symbols should not be exported from libc, but
* components reference them. These need to be fixed, too.
*/
extern int errno;
/*
* Between Solaris 2.5 and Solaris 9, __threaded was used to indicate
* "we are linked with libthread". The Sun Workshop 6 update 1 compilation
* system used it illegally (it is a consolidation private symbol).
* To accommodate this and possibly other abusers of the symbol,
* we make it always equal to 1 now that libthread has been folded
* into libc. The new __libc_threaded symbol is used to indicate
* the new meaning, "more than one thread exists".
*/
/*
* thr_concurrency and pthread_concurrency are not used by the library.
* They exist solely to hold and return the values set by calls to
* thr_setconcurrency() and pthread_setconcurrency().
* Because thr_concurrency is affected by the THR_NEW_LWP flag
* to thr_create(), thr_concurrency is protected by link_lock.
*/
static int pthread_concurrency;
/* initial allocation, just enough for one lwp */
};
extern const Lc_interface rtld_funcs[];
/*
* The weak version is known to libc_db and mdb.
*/
{ 0, }, /* tdb_hash_lock_stats */
{ { 0 }, }, /* siguaction[NSIG] */
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 },
{ DEFAULTMUTEX, NULL, 0 }},
{ DEFAULTMUTEX, {0, 0}, {0, 0} }, /* tls_metadata */
0, /* primary_map */
0, /* bucket_init */
0, /* pad[0] */
0, /* pad[1] */
{ 0 }, /* uberflags */
NULL, /* queue_head */
init_hash_table, /* thr_hash_table */
1, /* hash_size: size of the hash table */
0, /* hash_mask: hash_size - 1 */
NULL, /* ulwp_one */
NULL, /* all_lwps */
NULL, /* all_zombies */
0, /* nthreads */
0, /* nzombies */
0, /* ndaemons */
0, /* pid */
sigacthandler, /* sigacthandler */
NULL, /* lwp_stacks */
NULL, /* lwp_laststack */
0, /* nfreestack */
10, /* thread_stack_cache */
NULL, /* ulwp_freelist */
NULL, /* ulwp_lastfree */
NULL, /* ulwp_replace_free */
NULL, /* ulwp_replace_last */
NULL, /* atforklist */
NULL, /* robustlocks */
NULL, /* robustlist */
NULL, /* progname */
NULL, /* __tdb_bootstrap */
{ /* tdb */
NULL, /* tdb_sync_addr_hash */
0, /* tdb_register_count */
0, /* tdb_hash_alloc_failed */
NULL, /* tdb_sync_addr_free */
NULL, /* tdb_sync_addr_last */
0, /* tdb_sync_alloc */
{ 0, 0 }, /* tdb_ev_global_mask */
tdb_events, /* tdb_events array */
},
};
/*
* The weak version is known to libc_db and mdb.
*/
int thread_queue_dump = 0;
int thread_cond_wait_defer = 0;
int thread_error_detection = 0;
int thread_async_safe = 0;
int thread_door_noreserve = 0;
int thread_locks_misaligned = 0;
static ulwp_t *ulwp_alloc(void);
static size_t proc_stack_size(void);
/*
* Insert the lwp into the hash table.
*/
void
{
}
void
{
}
/*
* Delete the lwp from the hash table.
*/
void
{
;
}
void
{
int ix;
}
}
/*
* Retain stack information for thread structures that are being recycled for
* new threads. All other members of the thread structure should be zeroed.
*/
static void
{
}
static int stackprot;
/*
* Answer the question, "Is the lwp in question really dead?"
* We must inquire of the operating system to be really sure
* because the lwp may have called lwp_exit() but it has not
* yet completed the exit.
*/
static int
{
return (1);
return (1);
}
return (0);
}
/*
* Attempt to keep the stack cache within the specified cache limit.
*/
static void
{
if (dead_and_buried(ulwp)) {
udp->nfreestack--;
/*
* Now put the free ulwp on the ulwp freelist.
*/
else {
}
} else {
}
}
}
/*
* Find an unused stack of the requested size
* or create a new stack of the requested size.
* Return a pointer to the ulwp_t structure referring to the stack, or NULL.
* thr_exit() stores 1 in the ul_dead member.
* thr_join() stores -1 in the ul_lwpid member.
*/
static ulwp_t *
{
void *stk;
/*
* The stack is allocated PROT_READ|PROT_WRITE|PROT_EXEC
* unless overridden by the system's configuration.
*/
if (stackprot == 0) { /* do this once */
if (lprot <= 0)
}
if (pagesize == 0) /* do this once */
/*
* One megabyte stacks by default, but subtract off
* two pages for the system-created red zones.
* Round up a non-zero stack size to a pagesize multiple.
*/
if (stksize == 0)
else
/*
* Round up the mapping size to a multiple of pagesize.
* Note: mmap() provides at least one page of red zone
* so we deduct that from the value of guardsize.
*/
if (guardsize != 0)
dead_and_buried(ulwp)) {
/*
* The previous lwp is gone; reuse the stack.
* Remove the ulwp from the stack list.
*/
udp->nfreestack--;
return (ulwp);
}
}
/*
* None of the cached stacks matched our mapping size.
* Reduce the stack cache to get rid of possibly
* very old stacks that will never be reused.
*/
else if (udp->nfreestack > 0)
/*
* Create a new stack.
*/
/*
* We have allocated our stack. Now allocate the ulwp.
*/
ulwp = ulwp_alloc();
else {
if (guardsize) /* protect the extra red zone */
}
}
return (ulwp);
}
/*
* Get a ulwp_t structure from the free list or allocate a new one.
* Such ulwp_t's do not have a stack allocated by the library.
*/
static ulwp_t *
ulwp_alloc(void)
{
if (dead_and_buried(ulwp)) {
return (ulwp);
}
}
/* LINTED pointer cast may result in improper alignment */
}
return (ulwp);
}
/*
* Free a ulwp structure.
* If there is an associated stack, put it on the stack list and
* munmap() previously freed stacks up to the residual cache limit.
* Else put it on the ulwp free list and never call lfree() on it.
*/
static void
{
/*EMPTY*/;
else {
}
} else {
else {
}
}
}
/*
* Find a named lwp and return a pointer to its hash list location.
* On success, returns with the hash lock held.
*/
ulwp_t **
{
if (tid == 0)
return (NULL);
return (ulwpp);
}
return (NULL);
}
/*
* Wake up all lwps waiting on this lwp for some reason.
*/
void
{
}
/*
* Find a named lwp and return a pointer to it.
* Returns with the hash lock held.
*/
ulwp_t *
{
}
}
return (ulwp);
}
int
{
int error;
/*
* Enforce the restriction of not creating any threads
* until the primary link map has been initialized.
* Also, disallow thread creation to a child of vfork().
*/
return (ENOTSUP);
finish_init();
return (EINVAL);
return (ENOMEM);
} else {
/* initialize the private stack */
return (ENOMEM);
}
/* ulwp is not in the hash table; make sure hash_out() doesn't fail */
flags |= THR_DETACHED;
}
if (flags & THR_DAEMON)
lwp_flags |= LWP_DAEMON;
/* creating a thread: enforce mt-correctness in mutex_lock() */
/* per-thread copies of global variables, for speed */
/* new thread inherits creating thread's scheduling parameters */
/* debugger support */
#ifdef __sparc
/*
* We cache several instructions in the thread structure for use
* by the fasttrap DTrace provider. When changing this, read the
* comment in fasttrap.h for the all the other places that must
* be changed.
*/
#endif
/*
* Defer signals on the new thread until its TLS constructors
* have been called. _thrp_setup() will call sigon() after
* it has called tls_setup().
*/
/*
* Call enter_critical() to avoid being suspended until we
* have linked the new thread into the proper lists.
* This is necessary because forkall() and fork1() must
* suspend all threads and they must see a complete list.
*/
if (error != 0 ||
return (error);
}
if (new_thread)
*new_thread = tid;
if (flags & THR_DETACHED)
if (flags & THR_SUSPENDED)
if (flags & THR_DAEMON)
if (flags & THR_NEW_LWP)
}
if (!(flags & THR_SUSPENDED))
return (0);
}
int
{
}
/*
* A special cancellation cleanup hook for DCE.
* cleanuphndlr, when it is not NULL, will contain a callback
* function to be called before a thread is terminated in
* thr_exit() as a result of being cancelled.
*/
/*
* _pthread_setcleanupinit: sets the cleanup hook.
*/
int
{
cleanuphndlr = func;
return (0);
}
void
{
}
/*
* We are the last non-daemon thread exiting.
* Exit the process. We retain our TSD and TLS so
* that atexit() application functions can use them.
*/
exit(0);
/* NOTREACHED */
thr_panic("_thrp_exit(): exit(0) returned");
}
tsd_exit(); /* deallocate thread-specific data */
tls_exit(); /* deallocate thread-local storage */
heldlock_exit(); /* deal with left-over held locks */
/* block all signals to finish exiting */
/* also prevent ourself from being suspended */
/*
* We want to free the stack for reuse but must keep
* the ulwp_t struct for the benefit of thr_join().
* For this purpose we allocate a replacement ulwp_t.
*/
}
else {
}
#if defined(THREAD_DEBUG)
/* collect queue lock statistics before marking ourself dead */
#endif
self->ul_pleasestop = 0;
/*
* Having just changed the address of curthread, we
* must reset the ownership of the locks we hold so
* that assertions will not fire when we release them.
*/
/*
* NOTE:
* On i386, %gs still references the original, not the
* replacement, ulwp structure. Fetching the replacement
* curthread pointer via %gs:0 works correctly since the
* original ulwp structure will not be reallocated until
* this lwp has completed its lwp_exit() system call (see
* dead_and_buried()), but from here on out, we must make
* no references to %gs:<offset> other than %gs:0.
*/
}
/*
* Put non-detached terminated threads in the all_zombies list.
*/
if (!self->ul_detached) {
} else {
}
}
/*
* Notify everyone waiting for this thread.
*/
/*
* Prevent any more references to the schedctl data.
* We are exiting and continue_fork() may not find us.
* Do this just before dropping link_lock, since fork
* serializes on link_lock.
*/
_lwp_terminate(); /* never returns */
thr_panic("_thrp_exit(): _lwp_terminate() returned");
}
#if defined(THREAD_DEBUG)
void
{
if (thread_queue_dump) {
do {
}
}
}
#endif
static void __NORETURN
{
/*
* Disable cancellation and call the special DCE cancellation
* cleanup hook if it is enabled. Do nothing else before calling
* the DCE cancellation cleanup hook; it may call longjmp() and
* never return here.
*/
self->ul_cancel_async = 0;
self->ul_save_async = 0;
self->ul_cancelable = 0;
self->ul_cancel_pending = 0;
(*cleanuphndlr)();
/*
* Block application signals while we are exiting.
* We call out to C++, TSD, and TLS destructors while exiting
* and these are application-defined, so we cannot be assured
* that they won't reset the signal mask. We use sigoff() to
* defer any signals that may be received as a result of this
* bad behavior. Such signals will be lost to the process
* when the thread finishes exiting.
*/
/*
* If thr_exit is being called from the places where
* C++ destructors are to be called such as cancellation
* points, then set this flag. It is checked in _t_cancel()
* to decide whether _ex_unwind() is to be called or not.
*/
if (unwind)
/*
* _thrp_unwind() will eventually call _thrp_exit().
* It never returns.
*/
thr_panic("_thrp_exit_common(): _thrp_unwind() returned");
for (;;) /* to shut the compiler up about __NORETURN */
continue;
}
/*
* Called when a thread returns from its start function.
* We are at the top of the stack; no unwinding is necessary.
*/
void
{
_thrp_exit_common(status, 0);
}
void
{
}
int
{
void *rval;
int replace;
int error;
if (do_cancel)
else {
;
}
if (error)
return (error);
/*
* We must hold link_lock to avoid a race condition with find_stack().
*/
/*
* lwp_wait() found an lwp that the library doesn't know
* about. It must have been created with _lwp_create().
* Just return its lwpid; we can't know its status.
*/
} else {
/*
* Remove ulwp from the hash table.
*/
/*
* Remove ulwp from all_zombies list.
*/
else {
}
/*
* We can't call ulwp_unlock(ulwp) after we set
* ulwp->ul_ix = -1 so we have to get a pointer to the
* ulwp's hash table mutex now in order to unlock it below.
*/
if (replace) {
else {
}
}
}
return (0);
}
int
{
}
/*
* pthread_join() differs from Solaris thr_join():
* It does not return the departed thread's id
* and hence does not have a "departed" argument.
* It returns EINVAL if tid refers to a detached thread.
*/
int
{
}
int
{
int error = 0;
return (ESRCH);
} else {
}
return (error);
}
static const char *
{
int c;
while ((c = *match++) != '\0') {
if (*ev++ != c)
return (NULL);
}
if (*ev++ != '=')
return (NULL);
return (ev);
}
static int
{
const char *ename;
int c;
if (!isdigit(c)) {
val = -1;
break;
}
break;
}
}
}
return (val);
}
static void
{
int value;
#if defined(THREAD_DEBUG)
#endif
}
/*
* Look for and evaluate environment variables of the form "_THREAD_*".
* For compatibility with the past, we also look for environment
* names of the form "LIBTHREAD_*".
*/
static void
{
extern const char **_environ;
const char **pev;
const char *ev;
char c;
return;
c = *ev;
}
}
/* same as atexit() but private to the library */
extern int _atexit(void (*)(void));
/* same as _cleanup() but private to the library */
extern void __cleanup(void);
extern void atfork_init(void);
#ifdef __amd64
extern void __proc64id(void);
#endif
/*
* libc_init() is called by ld.so.1 for library initialization.
* We perform minimal initialization; enough to work with the main thread.
*/
void
libc_init(void)
{
int setmask;
/*
* For the initial stage of initialization, we must be careful
* not to call any function that could possibly call _cerror().
* For this purpose, we call only the raw system call wrappers.
*/
#ifdef __amd64
/*
* Gather information about cache layouts for optimized
* AMD and Intel assembler strfoo() and memfoo() functions.
*/
__proc64id();
#endif
/*
* Every libc, regardless of which link map, must register __cleanup().
*/
/*
* We keep our uberdata on one of (a) the first alternate link map
* or (b) the primary link map. We switch to the primary link map
* and stay there once we see it. All intermediate link maps are
* subject to being unloaded at any time.
*/
mutex_setup();
atfork_init(); /* every link map needs atfork() processing */
return;
}
/*
* To establish the main stack information, we have to get our context.
* This is also convenient to use for getting our signal mask.
*/
(void) __getcontext(&uc);
thr_panic("cannot allocate thread structure for main thread");
/* LINTED pointer cast may result in improper alignment */
/*
* Are the old and new sets different?
* (This can happen if we are currently blocking SIGCANCEL.)
* If so, we must explicitly set our signal mask, below.
*/
setmask =
#ifdef __sparc
/*
* We cache several instructions in the thread structure for use
* by the fasttrap DTrace provider. When changing this, read the
* comment in fasttrap.h for the all the other places that must
* be changed.
*/
#endif
int i;
for (i = 0; i < TSD_NFAST; i++)
/*
* Retrieve all pointers to uberdata allocated
* while running on previous link maps.
* We would like to do a structure assignment here, but
* gcc turns structure assignments into calls to memcpy(),
* a function exported from libc. We can't call any such
* external functions until we establish curthread, below,
* so we just call our private version of memcpy().
*/
/*
* These items point to global data on the primary link map.
*/
}
/*
* In every link map, tdb_bootstrap points to the same piece of
* allocated memory. When the primary link map is initialized,
* the allocated memory is assigned a pointer to the one true
* uberdata. This allows libc_db to initialize itself regardless
* of which instance of libc it finds in the address space.
*/
if (primary_link_map) {
}
/*
* Cancellation can't happen until:
* pthread_cancel() is called
* or:
* another thread is created
* For now, as a single-threaded process, set the flag that tells
*/
#if defined(__amd64)
#endif /* __i386 || __amd64 */
/*
* Now curthread is established and it is safe to call any
* function in libc except one that uses thread-local storage.
*/
/* tls_size was zero when oldself was allocated */
}
mutex_setup();
atfork_init();
signal_init();
/*
* If the stack is unlimited, we try reading
* If that fails, we just set it to 8 Mbytes.
*/
}
/*
* Get the variables that affect thread behavior from the environment.
*/
/*
* Make per-thread copies of global variables, for speed.
*/
if (self->ul_misaligned) {
/*
* refer to non-8-byte aligned data instead of giving
* the process an alignment trap and generating SIGBUS.
*
* Programs compiled for 32-bit sparc with the Studio SS12
* compiler get this done for them automatically (in _init()).
* We do it here for the benefit of programs compiled with
* other compilers, like gcc.
*
* This is necessary for the _THREAD_LOCKS_MISALIGNED=1
* environment variable horrible hack to work.
*/
extern void _do_fix_align(void);
}
#endif
/*
* When we have initialized the primary link map, inform
* the dynamic linker about our interface functions.
* Set up our pointer to the program name.
*/
if (self->ul_primarymap)
_ld_libc((void *)rtld_funcs);
/*
* Defer signals until TLS constructors have been called.
*/
tls_setup();
if (setmask)
(void) restore_signals(self);
/*
* Make private copies of __xpg4 and __xpg6 so libc can test
* them after this point without invoking the dynamic linker.
*/
libc__xpg4 = __xpg4;
libc__xpg6 = __xpg6;
init_aio();
/*
* We need to reset __threaded dynamically at runtime, so that
* __threaded can be bound to __threaded outside libc which may not
* have initial value of 1 (without a copy relocation in a.out).
*/
__threaded = 1;
}
void
{
/*
* If we are doing fini processing for the instance of libc
* on the first alternate link map (this happens only when
* the dynamic linker rejects a bad audit library), then clear
* __curthread(). We abandon whatever memory was allocated by
* lmalloc() while running on this alternate link-map but we
* don't care (and can't find the memory in any case); we just
* want to protect the application from this bad audit library.
* No fini processing is done by libc in the normal case.
*/
}
/*
* finish_init is called when we are about to become multi-threaded,
* that is, on the first call to thr_create().
*/
void
{
void *data;
int i;
/*
* No locks needed here; we are single-threaded on the first call.
* We can be called only after the primary link map has been set up.
*/
/*
* Initialize self->ul_policy, self->ul_cid, and self->ul_pri.
*/
/*
* Allocate the queue_head array if not already allocated.
*/
queue_alloc();
/*
* Now allocate the thread hash table.
*/
== MAP_FAILED)
thr_panic("cannot allocate thread hash table");
}
/*
* Set up the SIGCANCEL handler for threads cancellation.
*/
/*
* Arrange to do special things on exit --
* - collect queue statistics from all remaining active threads.
* - dump queue statistics to stderr if _THREAD_QUEUE_DUMP is set.
* - grab assert_lock to ensure that assertion failures
* and a core dump take precedence over _exit().
* (Functions are called in the reverse order of their registration.)
*/
(void) _atexit(grab_assert_lock);
#if defined(THREAD_DEBUG)
(void) _atexit(dump_queue_statistics);
(void) _atexit(collect_queue_statistics);
#endif
}
/*
* Used only by postfork1_child(), below.
*/
static void
{
}
/*
* This is called from fork1() in the child.
* Reset our data structures to reflect one lwp.
*/
void
{
int i;
/* daemon threads shouldn't call fork1(), but oh well... */
__libc_threaded = 0;
/*
* Some thread in the parent might have been suspended
* while holding udp->callout_lock or udp->ld_lock.
* Reinitialize the child's copies.
*/
/* no one in the child is on a sleep queue; reinitialize */
#if defined(THREAD_DEBUG)
#endif
}
}
/*
* Do post-fork1 processing for subsystems that need it.
* We need to do this before unmapping all of the abandoned
* threads' stacks, below(), because the post-fork1 actions
* might require access to those stacks.
*/
/*
* The above subsystems use thread pools, so this action
* must be performed after those actions.
*/
/*
* All lwps except ourself are gone. Mark them so.
* First mark all of the lwps that have already been freed.
* Then mark and free all of the active lwps except ourself.
* Since we are single-threaded, no locks are required here.
*/
}
do {
if (ulwp->ul_replace) {
} else {
}
}
}
trim_stack_cache(0);
}
lwp_self(void)
{
}
thr_self()
{
}
int
thr_main()
{
}
int
_thrp_cancelled(void)
{
}
int
{
return (0);
}
int
{
}
void
{
#if defined(THREAD_DEBUG)
#endif
int error;
for (;;) {
break;
error = 0;
}
break; /* so we are done */
/*
* He is marked as being in the process of stopping
* himself. Loop around and continue him again.
* He may not have been stopped the first time.
*/
}
}
/*
* Suspend an lwp with lwp_suspend(), then move it to a safe point,
* that is, to a point where ul_critical and ul_rtld are both zero.
* On return, the ulwp_lock() is dropped as with ulwp_unlock().
* If 'link_dropped' is non-NULL, then 'link_lock' is held on entry.
* If we have to drop link_lock, we store 1 through link_dropped.
* If the lwp exits before it can be suspended, we return ESRCH.
*/
int
{
int error = 0;
whystopped == TSTP_MUTATOR ||
whystopped == TSTP_FORK);
if (link_dropped != NULL)
*link_dropped = 0;
/*
* We must grab the target's spin lock before suspending it.
* See the comments below and in _thrp_suspend() for why.
*/
(void) ___lwp_suspend(tid);
top:
ulwp->ul_stopping) {
/* thread is already safe */
} else {
/*
* Setting ul_pleasestop causes the target thread to stop
* itself in _thrp_suspend(), below, after we drop its lock.
* We must continue the critical thread before dropping
* link_lock because the critical thread may be holding
* the queue lock for link_lock. This is delicate.
*/
if (link_dropped != NULL) {
*link_dropped = 1;
/* be sure to drop link_lock only once */
link_dropped = NULL;
}
/*
* The thread may disappear by calling thr_exit() so we
* cannot rely on the ulwp pointer after dropping the lock.
* Instead, we search the hash table to find it again.
* When we return, we may find that the thread has been
* interfaces are prone to such race conditions by design.
*/
break;
}
}
else {
/*
* Do another lwp_suspend() to make sure we don't
* return until the target thread is fully stopped
* in the kernel. Don't apply lwp_suspend() until
* we know that the target is not holding any
* queue locks, that is, that it has completed
* ulwp_unlock(self) and has, or at least is
* about to, call lwp_suspend() on itself. We do
* this by grabbing the target's spin lock.
*/
(void) ___lwp_suspend(tid);
/*
* If some other thread did a thr_continue()
* on the target thread we have to start over.
*/
goto top;
}
}
(void) cond_broadcast(cvp);
return (error);
}
int
{
int error = 0;
/*
* We can't suspend anyone except ourself while
* some other thread is performing a fork.
* This also allows only one suspension at a time.
*/
/*
* After suspending the other thread, move it out of a
* critical section and deal with the schedctl mappings.
* safe_suspend() suspends the other thread, calls
* ulwp_broadcast(ulwp) and drops the ulwp lock.
*/
} else {
int schedctl_after_fork = 0;
/*
* We are suspending ourself. We must not take a signal
* until we return from lwp_suspend() and clear ul_stopping.
* This is to guard against siglongjmp().
*/
_flush_windows(); /* sparc */
self->ul_pleasestop = 0;
/*
* Grab our spin lock before dropping ulwp_mutex(self).
* This prevents the suspending thread from applying
* lwp_suspend() to us before we emerge from
* lmutex_unlock(mp) and have dropped mp's queue lock.
*/
/*
* From this point until we return from lwp_suspend(),
* we must not call any function that might invoke the
* dynamic linker, that is, we can only call functions
* private to the library.
*
* Also, this is a nasty race condition for a process
* that is undergoing a forkall() operation:
* Once we clear our spinlock (below), we are vulnerable
* to being suspended by the forkall() thread before
* we manage to suspend ourself in ___lwp_suspend().
* See safe_suspend() and force_continue().
*
* To avoid a SIGSEGV due to the disappearance
* of the schedctl mappings in the child process,
* which can happen in spin_lock_clear() if we
* are suspended while we are in the middle of
* its call to preempt(), we preemptively clear
* our own schedctl pointer before dropping our
* spinlock. We reinstate it, in both the parent
* and (if this really is a forkall()) the child.
*/
if (whystopped & TSTP_FORK) {
schedctl_after_fork = 1;
}
(void) ___lwp_suspend(tid);
/*
* Somebody else continued us.
* We can't grab ulwp_lock(self)
* until after clearing ul_stopping.
* force_continue() relies on this.
*/
self->ul_stopping = 0;
if (schedctl_after_fork) {
(void) setup_schedctl();
}
}
return (error);
}
/*
* Suspend all lwps other than ourself in preparation for fork.
*/
void
{
int link_dropped;
top:
} else {
/*
* Move the stopped lwp out of a critical section.
*/
goto top;
}
}
}
void
{
/*
* Clear the schedctl pointers in the child of forkall().
*/
if (child) {
}
}
/*
* Set all lwps that were stopped for fork() running again.
*/
}
}
int
{
int error = 0;
whystopped == TSTP_MUTATOR);
/*
*/
return (ESRCH);
}
ulwp->ul_created = 0;
}
}
}
return (error);
}
int
{
}
int
{
}
void
{
yield();
}
int
{
return (EINVAL);
}
/*
* Exit a critical section, take deferred actions if necessary.
* Called from exit_critical() and from sigon().
*/
void
{
int sig;
/*
* Don't suspend ourself or take a deferred signal while dying
* or while executing inside the dynamic linker (ld.so.1).
*/
return;
while (self->ul_pleasestop ||
/*
* Avoid a recursive call to exit_critical() in _thrp_suspend()
* by keeping self->ul_critical == 1 here.
*/
self->ul_critical++;
while (self->ul_pleasestop) {
/*
* Guard against suspending ourself while on a sleep
* queue. See the comments in call_user_handler().
*/
unsleep_self();
set_parking_flag(self, 0);
}
self->ul_critical--;
/*
* Clear ul_cursig before proceeding.
* This protects us from the dynamic linker's
* calls to bind_guard()/bind_clear() in the
* event that it is invoked to resolve a symbol
* like take_deferred_signal() below.
*/
}
}
}
/*
* _ti_bind_guard() and _ti_bind_clear() are called by the dynamic linker
* (ld.so.1) when it has do do something, like resolve a symbol to be called
* by the application or one of its libraries. _ti_bind_guard() is called
* application. The dynamic linker gets special dispensation from libc to
* run in a critical region (all signals deferred and no thread suspension
* or forking allowed), and to be immune from cancellation for the duration.
*/
int
{
return (0);
}
return (1);
}
int
{
return (self->ul_bindflags);
}
}
return (self->ul_bindflags);
}
/*
* Tell the dynamic linker (ld.so.1) whether or not it was entered from
* a critical region in libc. Return zero if not, else return non-zero.
*/
int
_ti_critical(void)
{
return (level - 1);
}
/*
* sigoff() and sigon() enable cond_wait() to behave (optionally) like
* it does in the old libthread (see the comments in cond_wait_queue()).
* Also, signals are deferred at thread startup until TLS constructors
* have all been called, at which time _thrp_setup() calls sigon().
*
* _sigoff() and _sigon() are external consolidation-private interfaces to
* sigoff() and sigon(), respectively, in libc. These are used in libnsl.
* Also, _sigoff() and _sigon() are called from dbx's run-time checking
* (librtc.so) to defer signals during its critical sections (not to be
* confused with libc critical sections [see exit_critical() above]).
*/
void
_sigoff(void)
{
}
void
_sigon(void)
{
}
int
{
return (thr_concurrency);
}
int
{
return (pthread_concurrency);
}
int
{
if (new_level < 0)
return (EINVAL);
return (EAGAIN);
if (new_level > thr_concurrency)
return (0);
}
int
{
if (new_level < 0)
return (EINVAL);
return (EAGAIN);
return (0);
}
thr_min_stack(void)
{
return (MINSTACK);
}
int
__nthreads(void)
{
}
/*
* XXX
* The remainder of this file implements the private interfaces to java for
* garbage collection. It is no longer used, at least by java 1.2.
* It can all go away once all old JVMs have disappeared.
*/
/*
* Get the available register state for the target thread.
* Return non-volatile registers: TRS_NONVOLATILE
*/
int
{
int error = 0;
} else {
if (flag)
*flag = TRS_INVALID;
return (ESRCH);
}
}
if (flag)
if (lwp)
return (error);
}
/*
* Set the appropriate register state for the target thread.
* This is not used by java. It exists solely for the MSTC test suite.
*/
int
{
int error = 0;
return (ESRCH);
switch (flag) {
case TRS_NONVOLATILE:
/* do /proc stuff here? */
else
break;
case TRS_LWPID: /* do /proc stuff here? */
default:
break;
}
}
return (error);
}
int
{
int fd;
return (0);
}
yield(); /* give him a chance to stop */
}
}
return (-1);
}
int
{
int fd;
run_null[1] = 0;
return (0);
}
}
return (-1);
}
static size_t
proc_stack_size(void)
{
int fd;
goto out;
goto out;
break;
}
}
out:
if (fd >= 0)
return (size);
}
static ulong_t
{
/* "__gettsp(%u): can't read lwpstatus" w/o stdio */
}
}
{
return (0);
return (result);
}
return (result);
}
/*
* This tells java stack walkers how to find the ucontext
* structure passed to signal handlers.
*/
void
{
*func = &__sighndlr;
}
/*
* Mark a thread a mutator or reset a mutator to being a default,
* non-mutator thread.
*/
int
{
int error;
int cancel_state;
top:
if (tid == 0) {
return (ESRCH);
}
/*
* The target thread should be the caller itself or a suspended thread.
* This prevents the target from also changing its ul_mutator field.
*/
error = 0;
if (mutatorsbarrier) {
&cancel_state);
while (mutatorsbarrier)
goto top;
}
}
return (error);
}
/*
* Establish a barrier against new mutators. Any non-mutator trying
* to become a mutator is suspended until the barrier is removed.
*/
void
{
int oldvalue;
int cancel_state;
/*
* Wait if trying to set the barrier while it is already set.
*/
while (mutatorsbarrier && enabled)
/*
* Wakeup any blocked non-mutators when barrier is removed.
*/
(void) cond_broadcast(&mutatorscv);
}
/*
* Suspend the set of all mutators except for the caller. The list
* of actively running threads is searched and only the mutators
* in this list are suspended. Actively running non-mutators remain
* running. Any other thread is suspended.
*/
int
thr_suspend_allmutators(void)
{
int link_dropped;
/*
*/
top:
if (suspendingallmutators || suspendedallmutators) {
return (EINVAL);
}
if (!ulwp->ul_mutator) {
} else {
/*
* Move the stopped lwp out of a critical section.
*/
link_dropped) {
goto top;
}
}
}
suspendedallmutators = 1;
return (0);
}
/*
* Suspend the target mutator. The caller is permitted to suspend
* itself. If a mutator barrier is enabled, the caller will suspend
* itself as though it had been suspended by thr_suspend_allmutators().
* When the barrier is removed, this thread will be resumed. Any
* suspended mutator, whether suspended by thr_suspend_mutator(), or by
* thr_suspend_allmutators(), can be resumed by thr_continue_mutator().
*/
int
{
if (tid == 0)
}
/*
* Resume the set of all suspended mutators.
*/
int
{
/*
*/
if (!suspendedallmutators) {
return (EINVAL);
}
suspendedallmutators = 0;
}
}
return (0);
}
/*
* Resume a suspended mutator.
*/
int
{
}
int
{
int cancel_state;
int error = 0;
top:
return (ESRCH);
}
if (!ulwp->ul_mutator)
else if (dontwait) {
error = EWOULDBLOCK;
(void) lmutex_unlock(mp);
goto top;
}
return (error);
}
/* PROBE_SUPPORT begin */
void
{
}
static void *
{
}
/* ARGSUSED */
void
{
/* never called */
}
/* ARGSUSED */
void
{
/* never called */
}
/* PROBE_SUPPORT end */