[BACK]Return to pthread.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / lib / libpthread

Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.

Diff for /src/lib/libpthread/pthread.c between version 1.1 and 1.1.2.7

version 1.1, 2001/03/05 23:51:54 version 1.1.2.7, 2001/08/01 23:45:16
Line 0 
Line 1 
   /*      $NetBSD$        */
   
   /*-
    * Copyright (c) 2001 The NetBSD Foundation, Inc.
    * All rights reserved.
    *
    * This code is derived from software contributed to The NetBSD Foundation
    * by Nathan J. Williams.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions
    * are met:
    * 1. Redistributions of source code must retain the above copyright
    *    notice, this list of conditions and the following disclaimer.
    * 2. Redistributions in binary form must reproduce the above copyright
    *    notice, this list of conditions and the following disclaimer in the
    *    documentation and/or other materials provided with the distribution.
    * 3. All advertising materials mentioning features or use of this software
    *    must display the following acknowledgement:
    *        This product includes software developed by the NetBSD
    *        Foundation, Inc. and its contributors.
    * 4. Neither the name of The NetBSD Foundation nor the names of its
    *    contributors may be used to endorse or promote products derived
    *    from this software without specific prior written permission.
    *
    * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
    * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
    * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
    * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    * POSSIBILITY OF SUCH DAMAGE.
    */
   
   #include <assert.h>
   #include <err.h>
   #include <errno.h>
   #include <signal.h>
   #include <stdlib.h>
   #include <ucontext.h>
   
   #include "sched.h"
   #include "pthread.h"
   #include "pthread_int.h"
   
   
   static void     pthread__create_tramp(void *(*start)(void *), void *arg);
   
   static pthread_attr_t pthread_default_attr;
   
   pt_spin_t allqueue_lock;
   struct pt_queue_t allqueue;
   
   
   pt_spin_t deadqueue_lock;
   struct pt_queue_t deadqueue;
   struct pt_queue_t reidlequeue;
   
   
   extern struct pt_queue_t runqueue;
   extern struct pt_queue_t idlequeue;
   extern pt_spin_t runqueue_lock;
   
   static int started;
   
   #ifdef PTHREAD__DEBUG
   
   int pthread__debug_counters[PTHREADD_NCOUNTERS];
   
   #endif /* PTHREAD_DEBUG */
   
   
   static void
   pthread__start(void)
   {
           pthread_t first, idle;
           int i, ret;
   
   #ifdef PTHREAD__DEBUG
           pthread__debug_init();
   #endif
   
           /* Basic data structure setup */
           pthread_attr_init(&pthread_default_attr);
           PTQ_INIT(&allqueue);
           PTQ_INIT(&deadqueue);
           PTQ_INIT(&reidlequeue);
           PTQ_INIT(&runqueue);
           PTQ_INIT(&idlequeue);
   
           /* Create the thread structure corresponding to main() */
           pthread__initmain(&first);
           pthread__initthread(first);
           sigprocmask(0, NULL, &first->pt_sigmask);
           PTQ_INSERT_HEAD(&allqueue, first, pt_allq);
   
           for (i = 0; i < NIDLETHREADS; i++) {
                   /* Create idle threads */
                   ret = pthread__stackalloc(&idle);
                   if (ret != 0)
                           err(1, "Couldn't allocate stack for idle thread!");
                   pthread__initthread(idle);
                   PTQ_INSERT_HEAD(&allqueue, idle, pt_allq);
                   pthread__sched_idle(first, idle);
           }
   
           /* Start up the SA subsystem */
           pthread__sa_start();
   }
   
   /* General-purpose thread data structure sanitization. */
   void
   pthread__initthread(pthread_t t)
   {
           t->pt_magic = PT_MAGIC;
           t->pt_type = PT_THREAD_NORMAL;
           t->pt_state = PT_STATE_RUNNABLE;
           t->pt_spinlocks = 0;
           t->pt_next = NULL;
           t->pt_exitval = NULL;
           t->pt_flags = 0;
           t->pt_parent = NULL;
           t->pt_heldlock = NULL;
           t->pt_switchto = NULL;
           t->pt_sleepuc = NULL;
           sigemptyset(&t->pt_siglist);
           sigemptyset(&t->pt_sigmask);
           PTQ_INIT(&t->pt_joiners);
   #ifdef PTHREAD__DEBUG
           t->blocks = 0;
           t->preempts = 0;
           t->rescheds = 0;
   #endif
   }
   
   int
   pthread_create(pthread_t *thread, const pthread_attr_t *attr,
               void *(*startfunc)(void *), void *arg)
   {
           pthread_t self, newthread;
           pthread_attr_t nattr;
           int ret;
   
           PTHREADD_ADD(PTHREADD_CREATE);
           assert(thread != NULL);
   
           /* It's okay to check this without a lock because there can
            * only be one thread before it becomes true
            */
           if (started == 0) {
                   started = 1;
                   pthread__start();
           }
   
           if (attr == NULL)
                   nattr = pthread_default_attr;
           else if (((attr != NULL) && (attr->pta_magic == PT_ATTR_MAGIC)))
                   nattr = *attr;
           else
                   return EINVAL;
   
   
           self = pthread__self();
   
           /* 1. Set up a stack and allocate space for a pthread_st. */
           ret = pthread__stackalloc(&newthread);
           if (ret != 0)
                   return ret;
   
           /* 2. Set up state. */
           pthread__initthread(newthread);
           newthread->pt_flags = nattr.pta_flags;
           newthread->pt_sigmask = self->pt_sigmask;
   
           /* 3. Set up context. */
           /* The pt_uc pointer points to a location safely below the
            * stack start; this is arranged by pthread__stackalloc().
            */
           _getcontext_u(newthread->pt_uc);
           newthread->pt_uc->uc_stack = newthread->pt_stack;
           newthread->pt_uc->uc_link = NULL;
           makecontext(newthread->pt_uc, pthread__create_tramp, 2,
               startfunc, arg);
   
           /* 4. Add to queues. */
           pthread_spinlock(self, &allqueue_lock);
           PTQ_INSERT_HEAD(&allqueue, newthread, pt_allq);
           pthread_spinunlock(self, &allqueue_lock);
   
           pthread__sched(self, newthread);
   
           *thread = newthread;
   
           return 0;
   }
   
   static void
   pthread__create_tramp(void *(*start)(void *), void *arg)
   {
           void *retval;
   
           retval = start(arg);
   
           pthread_exit(retval);
   
           /* NOTREACHED */
   }
   
   
   /*
    * Other threads will switch to the idle thread so that they
    * can dispose of any awkward locks or recycle upcall state.
    */
   void
   pthread__idle(void)
   {
           pthread_t self;
   
           PTHREADD_ADD(PTHREADD_IDLE);
           self = pthread__self();
   
           /* The drill here is that we want to yield the processor,
            * but for the thread itself to be recovered, we need to be on
            * a list somewhere for the thread system to know about us.
            */
           pthread_spinlock(self, &deadqueue_lock);
           PTQ_INSERT_TAIL(&reidlequeue, self, pt_runq);
           self->pt_flags |= PT_FLAG_IDLED;
           pthread_spinunlock(self, &deadqueue_lock);
   
           /*
            * If we get to run this, then no preemption has happened
            * (because the upcall handler will not contiune an idle thread with
            * PT_FLAG_IDLED set), and so we can yield the processor safely.
            */
            sa_yield();
   }
   
   
   void
   pthread_exit(void *retval)
   {
           pthread_t self, joiner;
   
           self = pthread__self();
   
           self->pt_exitval = retval;
   
           pthread_spinlock(self, &self->pt_join_lock);
           if (self->pt_flags & PT_FLAG_DETACHED) {
                   pthread_spinunlock(self, &self->pt_join_lock);
   
                   pthread_spinlock(self, &allqueue_lock);
                   PTQ_REMOVE(&allqueue, self, pt_allq);
                   pthread_spinunlock(self, &allqueue_lock);
   
                   self->pt_state = PT_STATE_DEAD;
                   /* Yeah, yeah, doing work while we're dead is tacky. */
                   pthread_spinlock(self, &deadqueue_lock);
                   PTQ_INSERT_HEAD(&deadqueue, self, pt_allq);
                   pthread__block(self, &deadqueue_lock);
           } else {
                   self->pt_state = PT_STATE_ZOMBIE;
                   /* Wake up all the potential joiners. Only one can win.
                    * (Can you say "Thundering Herd"? I knew you could.)
                    */
                   PTQ_FOREACH(joiner, &self->pt_joiners, pt_sleep)
                       pthread__sched(self, joiner);
                   pthread__block(self, &self->pt_join_lock);
           }
   
   
           /* NOTREACHED */
           assert(0);
   }
   
   
   int
   pthread_join(pthread_t thread, void **valptr)
   {
           pthread_t self;
   
           if ((thread == NULL) || (thread->pt_magic != PT_MAGIC))
                   return EINVAL;
   
           self = pthread__self();
           pthread_spinlock(self, &thread->pt_join_lock);
   
           if (thread->pt_flags & PT_FLAG_DETACHED) {
                   pthread_spinunlock(self, &thread->pt_join_lock);
                   return EINVAL;
           }
   
           if ((thread->pt_state != PT_STATE_ZOMBIE) &&
               (thread->pt_state != PT_STATE_DEAD)) {
                   /*
                    * "I'm not dead yet!"
                    * "You will be soon enough."
                    */
                   PTQ_INSERT_TAIL(&thread->pt_joiners, self, pt_sleep);
   
                   self->pt_state = PT_STATE_BLOCKED;
                   pthread__block(self, &thread->pt_join_lock);
   
                   pthread_spinlock(self, &thread->pt_join_lock);
           }
   
           if ((thread->pt_state == PT_STATE_DEAD) ||
               (thread->pt_flags & PT_FLAG_DETACHED)) {
                   /* Someone beat us to the join, or called pthread_detach(). */
                   pthread_spinunlock(self, &thread->pt_join_lock);
                   return ESRCH;
           }
   
           /* All ours. */
           thread->pt_state = PT_STATE_DEAD;
           pthread_spinunlock(self, &thread->pt_join_lock);
   
           if (valptr != NULL)
                   *valptr = thread->pt_exitval;
   
           /* Cleanup time. Move the dead thread from allqueue to the deadqueue */
           pthread_spinlock(self, &allqueue_lock);
           PTQ_REMOVE(&allqueue, thread, pt_allq);
           pthread_spinunlock(self, &allqueue_lock);
   
           pthread_spinlock(self, &deadqueue_lock);
           PTQ_INSERT_HEAD(&deadqueue, thread, pt_allq);
           pthread_spinunlock(self, &deadqueue_lock);
   
           return 0;
   }
   
   
   int
   pthread_equal(pthread_t t1, pthread_t t2)
   {
   
           /* Nothing special here. */
           return (t1 == t2);
   }
   
   int
   pthread_detach(pthread_t thread)
   {
           pthread_t self, joiner;
   
           if ((thread == NULL) || (thread->pt_magic != PT_MAGIC))
                   return EINVAL;
   
           self = pthread__self();
           pthread_spinlock(self, &thread->pt_join_lock);
   
           if (thread->pt_flags & PT_FLAG_DETACHED) {
                   pthread_spinunlock(self, &thread->pt_join_lock);
                   return EINVAL;
           }
   
           thread->pt_flags |= PT_FLAG_DETACHED;
   
           /* Any joiners have to be punted now. */
           PTQ_FOREACH(joiner, &thread->pt_joiners, pt_sleep)
               pthread__sched(self, joiner);
   
           pthread_spinunlock(self, &thread->pt_join_lock);
   
           return 0;
   }
   
   int
   sched_yield(void)
   {
           /* XXX implement me */
           return 0;
   }
   
   
   
   int
   pthread_attr_init(pthread_attr_t *attr)
   {
   
           attr->pta_magic = PT_ATTR_MAGIC;
           attr->pta_flags = 0;
   
           return 0;
   }
   
   
   int
   pthread_attr_destroy(pthread_attr_t *attr)
   {
   
           return 0;
   }
   
   
   int
   pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate)
   {
   
           if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
                   return EINVAL;
   
           *detachstate = (attr->pta_flags & PT_FLAG_DETACHED);
   
           return 0;
   }
   
   
   int
   pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)
   {
           if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
                   return EINVAL;
   
           switch (detachstate) {
           case PTHREAD_CREATE_JOINABLE:
                   attr->pta_flags &= ~PT_FLAG_DETACHED;
                   break;
           case PTHREAD_CREATE_DETACHED:
                   attr->pta_flags |= PT_FLAG_DETACHED;
                   break;
           default:
                   return EINVAL;
           }
   
           return 0;
   }
   
   
   int
   pthread_attr_setschedparam(pthread_attr_t *attr,
       const struct sched_param *param)
   {
   
           if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
                   return EINVAL;
   
           if (param == NULL)
                   return EINVAL;
   
           if (param->sched_priority != 0)
                   return EINVAL;
   
           return 0;
   }
   
   
   int
   pthread_attr_getschedparam(pthread_attr_t *attr, struct sched_param *param)
   {
   
           if ((attr == NULL) || (attr->pta_magic != PT_ATTR_MAGIC))
                   return EINVAL;
   
           if (param == NULL)
                   return EINVAL;
   
           param->sched_priority = 0;
   
           return 0;
   }
   
   
   
   void *
   pthread__malloc(size_t size)
   {
           /* XXX locking */
           return malloc(size);
   }
   
   
   void
   pthread__free(void *ptr)
   {
           /* XXX locking */
           free(ptr);
   }
   
   /* XXX There should be a way for applications to use the efficent
    *  inline version, but there are opacity/namespace issues.
    */
   
   pthread_t
   pthread_self(void)
   {
           return pthread__self();
   }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.1.2.7

CVSweb <webmaster@jp.NetBSD.org>