OSSP CVS Repository

ossp - Check-in [2718]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 2718
Date: 2002-Nov-03 10:59:32 (local)
2002-Nov-03 09:59:32 (UTC)
User:rse
Branch:
Comment: Added a stand-alone sub-API for manual user-space context switching. It is somewhat modeled after the POSIX ucontext(3) facility and consists of an opaque data type pth_uctx_t and the management functions pth_uctx_create(), pth_uctx_make(), pth_uctx_save(), pth_uctx_restore(), pth_uctx_switch() and pth_uctx_destroy(). These functions are based on the same underlying machine context switching facility (pth_mctx) the threads in GNU Pth are using. This facility can be used to implement co-routines without a full real multithreading environment or even to implement an own multithreading environment.
Tickets:
Inspections:
Files:
ossp-pkg/pth/.cvsignore      1.20 -> 1.21     1 inserted, 0 deleted
ossp-pkg/pth/ChangeLog      1.582 -> 1.583     13 inserted, 0 deleted
ossp-pkg/pth/Makefile.in      1.147 -> 1.148     11 inserted, 4 deleted
ossp-pkg/pth/pth.h.in      1.134 -> 1.135     12 inserted, 0 deleted
ossp-pkg/pth/pth.pod      1.157 -> 1.158     89 inserted, 0 deleted
ossp-pkg/pth/pth_uctx.c      added-> 1.1
ossp-pkg/pth/test_uctx.c      added-> 1.1

ossp-pkg/pth/.cvsignore 1.20 -> 1.21

--- .cvsignore   2002/01/30 13:05:22     1.20
+++ .cvsignore   2002/11/03 09:59:32     1.21
@@ -36,5 +36,6 @@
 test_pthread
 test_select
 test_sfio
+test_uctx
 test_sig
 test_std


ossp-pkg/pth/ChangeLog 1.582 -> 1.583

--- ChangeLog    2002/10/25 11:56:16     1.582
+++ ChangeLog    2002/11/03 09:59:32     1.583
@@ -21,6 +21,19 @@
 
   Changes between 1.4.1 and 1.5.0 (27-Jan-2002 to xx-Oct-2002)
 
+   *) Added a stand-alone sub-API for manual user-space context
+      switching. It is somewhat modeled after the POSIX ucontext(3)
+      facility and consists of an opaque data type pth_uctx_t and
+      the management functions pth_uctx_create(), pth_uctx_make(),
+      pth_uctx_save(), pth_uctx_restore(), pth_uctx_switch() and
+      pth_uctx_destroy(). These functions are based on the same
+      underlying machine context switching facility (pth_mctx)
+      the threads in GNU Pth are using. This facility can be used
+      to implement co-routines without a full real multithreading
+      environment or even to implement an own multithreading
+      environment.
+      [Ralf S. Engelschall]
+
    *) Add a Pth variant of the new POSIX pselect(2) function, including
       soft and hard syscall mapping support for it.
       [Ralf S. Engelschall]


ossp-pkg/pth/Makefile.in 1.147 -> 1.148

--- Makefile.in  2002/10/15 20:34:22     1.147
+++ Makefile.in  2002/11/03 09:59:32     1.148
@@ -88,19 +88,19 @@
 TARGET_LIBS = libpth.la @LIBPTHREAD_LA@
 TARGET_MANS = $(S)pth-config.1 $(S)pth.3 @PTHREAD_CONFIG_1@ @PTHREAD_3@
 TARGET_TEST = test_std test_mp test_misc test_philo test_sig \
-              test_select test_httpd test_sfio @TEST_PTHREAD@
+              test_select test_httpd test_sfio test_uctx @TEST_PTHREAD@
 
 #   object files for library generation
 #   (order is just aesthically important)
-LOBJS = pth_debug.lo pth_ring.lo pth_pqueue.lo pth_time.lo pth_errno.lo \
-        pth_mctx.lo pth_tcb.lo pth_sched.lo pth_attr.lo pth_lib.lo pth_event.lo \
+LOBJS = pth_debug.lo pth_ring.lo pth_pqueue.lo pth_time.lo pth_errno.lo pth_mctx.lo \
+        pth_uctx.lo pth_tcb.lo pth_sched.lo pth_attr.lo pth_lib.lo pth_event.lo \
         pth_data.lo pth_clean.lo pth_cancel.lo pth_msg.lo pth_sync.lo pth_fork.lo \
         pth_util.lo pth_high.lo pth_syscall.lo pth_ext.lo pth_compat.lo pth_string.lo
 
 #   source files for header generation
 #   (order is important and has to follow dependencies in pth_p.h)
 HSRCS = $(S)pth_compat.c $(S)pth_debug.c $(S)pth_syscall.c $(S)pth_errno.c $(S)pth_ring.c $(S)pth_mctx.c \
-        $(S)pth_clean.c $(S)pth_time.c $(S)pth_tcb.c $(S)pth_util.c $(S)pth_pqueue.c $(S)pth_event.c \
+        $(S)pth_uctx.c $(S)pth_clean.c $(S)pth_time.c $(S)pth_tcb.c $(S)pth_util.c $(S)pth_pqueue.c $(S)pth_event.c \
         $(S)pth_sched.c $(S)pth_data.c $(S)pth_msg.c $(S)pth_cancel.c $(S)pth_sync.c $(S)pth_attr.c $(S)pth_lib.c \
         $(S)pth_fork.c $(S)pth_high.c $(S)pth_ext.c $(S)pth_string.c
 
@@ -209,6 +209,8 @@
         $(LIBTOOL) --mode=link --quiet $(CC) $(LDFLAGS) -o test_select test_select.o test_common.o libpth.la $(LIBS)
 test_sfio: test_sfio.o test_common.o libpth.la
         $(LIBTOOL) --mode=link --quiet $(CC) $(LDFLAGS) -o test_sfio test_sfio.o test_common.o libpth.la $(LIBS)
+test_uctx: test_uctx.o test_common.o libpth.la
+        $(LIBTOOL) --mode=link --quiet $(CC) $(LDFLAGS) -o test_uctx test_uctx.o test_common.o libpth.la $(LIBS)
 test_pthread: test_pthread.o test_common.o libpthread.la
         $(LIBTOOL) --mode=link --quiet $(CC) $(LDFLAGS) -o test_pthread test_pthread.o test_common.o libpthread.la $(LIBS)
 
@@ -371,6 +373,8 @@
         ./test_select
 test-sfio: test_sfio
         ./test_sfio
+test-uctx: test_uctx
+        ./test_uctx
 test-pthread: test_pthread
         ./test_pthread
 debug: debug-std
@@ -390,6 +394,8 @@
         TEST=test_select && $(_DEBUG)
 debug-sfio: test_sfio
         TEST=test_sfio && $(_DEBUG)
+debug-uctx: test_uctx
+        TEST=test_uctx && $(_DEBUG)
 debug-pthread: test_pthread
         TEST=test_pthread && $(_DEBUG)
 
@@ -445,5 +451,6 @@
 test_pthread.o: test_pthread.c pthread.h
 test_select.o: test_select.c pth.h
 test_sfio.o: test_sfio.c pth.h
+test_uctx.o: test_uctx.c pth.h
 test_sig.o: test_sig.c pth.h
 test_std.o: test_std.c pth.h


ossp-pkg/pth/pth.h.in 1.134 -> 1.135

--- pth.h.in     2002/10/25 11:56:16     1.134
+++ pth.h.in     2002/11/03 09:59:32     1.135
@@ -306,6 +306,10 @@
     pth_mutex_t   br_mutex;
 };
 
+    /* the user-space context structure */
+typedef struct pth_uctx_st *pth_uctx_t;
+struct pth_uctx_st;
+
     /* filedescriptor blocking modes */
 enum {
     PTH_FDMODE_ERROR = -1,
@@ -475,6 +479,14 @@
 extern int            pth_barrier_init(pth_barrier_t *, int);
 extern int            pth_barrier_reach(pth_barrier_t *);
 
+    /* user-space context functions */
+extern int            pth_uctx_create(pth_uctx_t *);
+extern int            pth_uctx_make(pth_uctx_t, char *, size_t, const sigset_t *, void (*)(void *), void *, pth_uctx_t);
+extern int            pth_uctx_save(pth_uctx_t);
+extern int            pth_uctx_restore(pth_uctx_t);
+extern int            pth_uctx_switch(pth_uctx_t, pth_uctx_t);
+extern int            pth_uctx_destroy(pth_uctx_t);
+
     /* extension functions */
 extern Sfdisc_t      *pth_sfiodisc(void);
 


ossp-pkg/pth/pth.pod 1.157 -> 1.158

--- pth.pod      2002/10/25 11:56:16     1.157
+++ pth.pod      2002/11/03 09:59:33     1.158
@@ -138,6 +138,15 @@
 pth_barrier_init,
 pth_barrier_reach.
 
+=item B<User-Space Context>
+
+pth_uctx_create,
+pth_uctx_make,
+pth_uctx_save,
+pth_uctx_restore,
+pth_uctx_switch,
+pth_uctx_destroy.
+
 =item B<Generalized POSIX Replacement API>
 
 pth_sigwait_ev,
@@ -1464,6 +1473,86 @@
 
 =back
 
+=head2 User-Space Context
+
+The following functions provide a stand-alone sub-API for user-space
+context switching. It internally is based on the same underlying machine
+context switching mechanism the threads in B<GNU Pth> are based on.
+Hence these functions you can use for implementing your own simple
+user-space threads. The C<pth_uctx_t> context is somewhat modeled after
+POSIX ucontext(3).
+
+The time required to create (via pth_uctx_make(3)) a user-space context
+can range from just a few microseconds up to a more dramatical time
+(depending on the machine context switching method which is available on
+the platform). On the other hand, the raw performance in switching the
+user-space contexts is always very good (nearly independent of the used
+machine context switching method). For instance, on an Intel Pentium-III
+CPU with 800Mhz running under FreeBSD 4 one usually achieves about
+260,000 user-space context switches (via pth_uctx_switch(3)) per second.
+
+=over 4
+
+=item int B<pth_uctx_create>(pth_uctx_t *I<uctx>);
+
+This function creates a user-space context and stores it into I<uctx>.
+There is still no underlying user-space context configured. You still
+have to do this with pth_uctx_make(3) or pth_uctx_set(3). On success,
+this function returns C<TRUE>, else C<FALSE>.
+
+=item int B<pth_uctx_make>(pth_uctx_t I<uctx>, char *I<sk_addr>, size_t I<sk_size>, const sigset_t *I<sigmask>, void (*I<start_func>)(void *), void *I<start_arg>, pth_uctx_t I<uctx_after>);
+
+This function makes a new user-space context in I<uctx> which will
+operate on the run-time stack I<sk_addr> (which is of maximum
+size I<sk_size>), with the signals in I<sigmask> blocked (if
+I<sigmask> is not C<NULL>) and starting to execute with the call
+I<start_func>(I<start_arg>). If I<sk_addr> is C<NULL>, a stack
+is dynamically allocated. The stack size I<sk_size> has to be at
+least 16384 (16KB). If the start function I<start_func> returns and
+I<uctx_after> is not C<NULL>, an implicit user-space context switch
+to this context is performed. Else (if I<uctx_after> is C<NULL>) the
+process is terminated with exit(3). This function is somewhat modeled
+after POSIX makecontext(3). On success, this function returns C<TRUE>,
+else C<FALSE>.
+
+=item int B<pth_uctx_save>(pth_uctx_t I<uctx>);
+
+This function saves the current user-space context in I<uctx> for later
+restoring by either pth_uctx_restore(3) or pth_uctx_switch(3). This
+function is somewhat modeled after POSIX getcontext(3). If I<uctx> is
+C<NULL>, C<FALSE> is returned instead of C<TRUE>. This is the only error
+possible.
+
+=item int B<pth_uctx_restore>(pth_uctx_t I<uctx>);
+
+This function restores the current user-space context from I<uctx>,
+which previously had to be set with either pth_uctx_make(3) or
+pth_uctx_save(3). This function is somewhat modeled after POSIX
+setcontext(3). If I<uctx> is C<NULL> or I<uctx> contains no valid
+user-space context, C<FALSE> is returned instead of C<TRUE>. These are
+the only errors possible.
+
+=item int B<pth_uctx_switch>(pth_uctx_t I<uctx_from>, pth_uctx_t I<uctx_to>);
+
+This function saves the current user-space context in I<uctx_from> for
+later restoring by either pth_uctx_restore(3) or pth_uctx_switch(3) and
+restores the new user-space context from I<uctx>, which previously
+had to be set with either pth_uctx_make(3) or pth_uctx_save(3). This
+function is somewhat modeled after POSIX swapcontext(3). If I<uctx_from>
+or I<uctx_to> are C<NULL> or if I<uctx_to> contains no valid user-space
+context, C<FALSE> is returned instead of C<TRUE>. These are the only
+errors possible.
+
+=item int B<pth_uctx_destroy>(pth_uctx_t I<uctx>);
+
+This function destroys the user-space context in I<uctx>. The run-time
+stack associated with the user-space context is deallocated only if it
+was given by the application (see I<sk_addr> of pth_uctx_create(3)).
+If I<uctx> is C<NULL>, C<FALSE> is returned instead of C<TRUE>. This
+is the only error possible.
+
+=back
+
 =head2 Generalized POSIX Replacement API
 
 The following functions are generalized replacements functions for the POSIX


ossp-pkg/pth/pth_uctx.c -> 1.1

*** /dev/null    Mon Apr 29 12:33:30 2024
--- -    Mon Apr 29 12:35:53 2024
***************
*** 0 ****
--- 1,235 ----
+ /*
+ **  GNU Pth - The GNU Portable Threads
+ **  Copyright (c) 1999-2002 Ralf S. Engelschall <rse@engelschall.com>
+ **
+ **  This file is part of GNU Pth, a non-preemptive thread scheduling
+ **  library which can be found at http://www.gnu.org/software/pth/.
+ **
+ **  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 2.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ **  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
+ **
+ **  pth_uctx.c: Pth user-space context handling (stand-alone sub-API)
+ */
+                              /* ``It worries me however, to realize
+                                 how tough an ass-hole I have had to
+                                 be, in order to get to stick to the
+                                 principle of doing things right,
+                                 rather than "just hack it in".''
+                                 -- Poul-Henning Kamp <phk@FreeBSD.org> */
+ #include "pth_p.h"
+ 
+ /* user-space context structure */
+ struct pth_uctx_st {
+     int         uc_stack_own; /* whether stack were allocated by us */
+     char       *uc_stack_ptr; /* pointer to start address of stack area */
+     size_t      uc_stack_len; /* size of stack area */
+     int         uc_mctx_set;  /* whether uc_mctx is set */
+     pth_mctx_t  uc_mctx;      /* saved underlying machine context */
+ };
+ 
+ /* create user-space context structure */
+ int
+ pth_uctx_create(
+     pth_uctx_t *puctx)
+ {
+     pth_uctx_t uctx;
+ 
+     /* argument sanity checking */
+     if (puctx == NULL)
+         return pth_error(FALSE, EINVAL);
+ 
+     /* allocate the context structure */
+     if ((uctx = (pth_uctx_t)malloc(sizeof(struct pth_uctx_st))) == NULL)
+         return pth_error(FALSE, errno);
+ 
+     /* initialize the context structure */
+     uctx->uc_stack_own = FALSE;
+     uctx->uc_stack_ptr = NULL;
+     uctx->uc_stack_len = 0;
+     uctx->uc_mctx_set  = FALSE;
+     memset((void *)&uctx->uc_mctx, 0, sizeof(pth_mctx_t));
+ 
+     /* pass result to caller */
+     *puctx = uctx;
+ 
+     return TRUE;
+ }
+ 
+ /* trampoline context */
+ typedef struct {
+     pth_mctx_t *mctx_parent;
+     pth_uctx_t  uctx_this;
+     pth_uctx_t  uctx_after;
+     void      (*start_func)(void *);
+     void       *start_arg;
+ } pth_uctx_trampoline_t;
+ pth_uctx_trampoline_t pth_uctx_trampoline_ctx;
+ 
+ /* trampoline function for pth_uctx_make() */
+ static void pth_uctx_trampoline(void)
+ {
+     volatile pth_uctx_trampoline_t ctx;
+ 
+     /* move context information from global to local storage */
+     ctx.mctx_parent = pth_uctx_trampoline_ctx.mctx_parent;
+     ctx.uctx_this   = pth_uctx_trampoline_ctx.uctx_this;
+     ctx.uctx_after  = pth_uctx_trampoline_ctx.uctx_after;
+     ctx.start_func  = pth_uctx_trampoline_ctx.start_func;
+     ctx.start_arg   = pth_uctx_trampoline_ctx.start_arg;
+ 
+     /* switch back to parent */
+     pth_mctx_switch(&(ctx.uctx_this->uc_mctx), ctx.mctx_parent);
+ 
+     /* enter start function */
+     (*ctx.start_func)(ctx.start_arg);
+ 
+     /* switch to successor user-space context */
+     if (ctx.uctx_after != NULL)
+         pth_uctx_restore(ctx.uctx_after);
+ 
+     /* terminate process (the only reasonable thing to do here) */
+     exit(0);
+ 
+     /* NOTREACHED */
+     return;
+ }
+ 
+ /* make setup of user-space context structure */
+ int
+ pth_uctx_make(
+     pth_uctx_t uctx,
+     char *sk_addr, size_t sk_size,
+     const sigset_t *sigmask,
+     void (*start_func)(void *), void *start_arg,
+     pth_uctx_t uctx_after)
+ {
+     pth_mctx_t mctx_parent;
+     sigset_t ss;
+ 
+     /* argument sanity checking */
+     if (uctx == NULL || start_func == NULL || sk_size < 16*1024)
+         return pth_error(FALSE, EINVAL);
+ 
+     /* configure run-time stack */
+     if (sk_addr == NULL) {
+         if ((sk_addr = (char *)malloc(sk_size)) == NULL)
+             return pth_error(FALSE, errno);
+         uctx->uc_stack_own = TRUE;
+     }
+     else
+         uctx->uc_stack_own = FALSE;
+     uctx->uc_stack_ptr = sk_addr;
+     uctx->uc_stack_len = sk_size;
+ 
+     /* configure the underlying machine context */
+     if (!pth_mctx_set(&uctx->uc_mctx, pth_uctx_trampoline,
+                       uctx->uc_stack_ptr, uctx->uc_stack_ptr+uctx->uc_stack_len))
+         return pth_error(FALSE, errno);
+ 
+     /* move context information into global storage for the trampoline jump */
+     pth_uctx_trampoline_ctx.mctx_parent = &mctx_parent;
+     pth_uctx_trampoline_ctx.uctx_this   = uctx;
+     pth_uctx_trampoline_ctx.uctx_after  = uctx_after;
+     pth_uctx_trampoline_ctx.start_func  = start_func;
+     pth_uctx_trampoline_ctx.start_arg   = start_arg;
+ 
+     /* optionally establish temporary signal mask */
+     if (sigmask != NULL)
+         sigprocmask(SIG_SETMASK, sigmask, &ss);
+ 
+     /* perform the trampoline step */
+     pth_mctx_switch(&mctx_parent, &uctx->uc_mctx);
+ 
+     /* optionally restore original signal mask */
+     if (sigmask != NULL)
+         sigprocmask(SIG_SETMASK, &ss, NULL);
+ 
+     /* finally flag that the context is now configured */
+     uctx->uc_mctx_set = TRUE;
+ 
+     return TRUE;
+ }
+ 
+ /* save current user-space context */
+ int
+ pth_uctx_save(
+     pth_uctx_t uctx)
+ {
+     /* argument sanity checking */
+     if (uctx == NULL)
+         return pth_error(FALSE, EINVAL);
+ 
+     /* save underlying machine context */
+     pth_mctx_save(&uctx->uc_mctx);
+     uctx->uc_mctx_set = TRUE;
+ 
+     return TRUE;
+ }
+ 
+ /* restore current user-space context */
+ int
+ pth_uctx_restore(
+     pth_uctx_t uctx)
+ {
+     /* argument sanity checking */
+     if (uctx == NULL)
+         return pth_error(FALSE, EINVAL);
+     if (!(uctx->uc_mctx_set))
+         return pth_error(FALSE, EPERM);
+ 
+     /* restore underlying machine context */
+     pth_mctx_restore(&uctx->uc_mctx);
+ 
+     return TRUE;
+ }
+ 
+ /* switch from current to other user-space context */
+ int
+ pth_uctx_switch(
+     pth_uctx_t uctx_from,
+     pth_uctx_t uctx_to)
+ {
+     /* argument sanity checking */
+     if (uctx_from == NULL || uctx_to == NULL)
+         return pth_error(FALSE, EINVAL);
+     if (!(uctx_to->uc_mctx_set))
+         return pth_error(FALSE, EPERM);
+ 
+     /* switch underlying machine context */
+     uctx_from->uc_mctx_set = TRUE;
+     pth_mctx_switch(&uctx_from->uc_mctx, &uctx_to->uc_mctx);
+ 
+     return TRUE;
+ }
+ 
+ /* destroy user-space context structure */
+ int
+ pth_uctx_destroy(
+     pth_uctx_t uctx)
+ {
+     /* argument sanity checking */
+     if (uctx == NULL)
+         return pth_error(FALSE, EINVAL);
+ 
+     /* deallocate dynamically allocated stack */
+     if (uctx->uc_stack_own && uctx->uc_stack_ptr != NULL)
+         free(uctx->uc_stack_ptr);
+ 
+     /* deallocate context structure */
+     free(uctx);
+ 
+     return TRUE;
+ }
+ 


ossp-pkg/pth/test_uctx.c -> 1.1

*** /dev/null    Mon Apr 29 12:33:30 2024
--- -    Mon Apr 29 12:35:53 2024
***************
*** 0 ****
--- 1,151 ----
+ /*
+ **  GNU Pth - The GNU Portable Threads
+ **  Copyright (c) 1999-2002 Ralf S. Engelschall <rse@engelschall.com>
+ **
+ **  This file is part of GNU Pth, a non-preemptive thread scheduling
+ **  library which can be found at http://www.gnu.org/software/pth/.
+ **
+ **  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 2.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ **  USA, or contact Ralf S. Engelschall <rse@engelschall.com>.
+ **
+ **  test_uctx.c: Pth test program (user-space context switching)
+ */
+                              /* ``Engineering does not require science.
+                                 Science helps a lot, but people built
+                                 perfectly good brick walls long before
+                                 they knew why cement works.''
+                                                         -- Alan Cox */
+ #include <stdio.h>
+ #include <time.h>
+ 
+ #include "pth.h"
+ 
+ volatile pth_uctx_t uctx[10];
+ 
+ /*
+  *  Test 1: master and worker "threads"
+  */
+ 
+ volatile int worker_done[10];
+ 
+ static void worker(void *ctx)
+ {
+     volatile int n = (int)ctx;
+     volatile int i = 0;
+ 
+     fprintf(stderr, "worker #%d: enter\n", n);
+     for (i = 0; i < 100; i++) {
+         fprintf(stderr, "worker #%d: working (step %d)\n", n, i);
+         pth_uctx_switch(uctx[n], uctx[0]);
+     }
+     worker_done[n] = TRUE;
+     fprintf(stderr, "worker #%d: exit\n", n);
+     return;
+ }
+ 
+ static void test_working(void)
+ {
+     volatile int i;
+     volatile int todo;
+ 
+     fprintf(stderr, "master: startup\n");
+ 
+     fprintf(stderr, "master: create contexts\n");
+     pth_uctx_create((pth_uctx_t *)&uctx[0]);
+     worker_done[0] = FALSE;
+     for (i = 1; i < 10; i++) {
+         worker_done[i] = FALSE;
+         pth_uctx_create((pth_uctx_t *)&uctx[i]);
+         pth_uctx_make(uctx[i], NULL, 32*1024, NULL, worker, (void *)i, uctx[0]);
+     }
+ 
+     do {
+         todo = 0;
+         for (i = 1; i < 10; i++) {
+             if (!worker_done[i]) {
+                 fprintf(stderr, "master: switching to worker #%d\n", i);
+                 pth_uctx_switch(uctx[0], uctx[i]);
+                 fprintf(stderr, "master: came back from worker #%d\n", i);
+                 todo = 1;
+             }
+         }
+     } while (todo);
+ 
+     fprintf(stderr, "master: destroy contexts\n");
+     for (i = 1; i < 10; i++)
+         pth_uctx_destroy(uctx[i]);
+     pth_uctx_destroy(uctx[0]);
+ 
+     fprintf(stderr, "master: exit\n");
+     return;
+ }
+ 
+ /*
+  *  Test 2: raw switching performance
+  */
+ 
+ #define DO_SWITCHES 10000000
+ 
+ time_t       stat_start;
+ time_t       stat_end;
+ volatile int stat_switched;
+ 
+ static void dummy(void *ctx)
+ {
+     while (1) {
+         stat_switched++;
+         pth_uctx_switch(uctx[1], uctx[0]);
+     }
+     return;
+ }
+ 
+ static void test_performance(void)
+ {
+     volatile int i;
+ 
+     pth_uctx_create((pth_uctx_t *)&uctx[0]);
+     pth_uctx_create((pth_uctx_t *)&uctx[1]);
+     pth_uctx_make(uctx[1], NULL, 32*1024, NULL, dummy, NULL, uctx[0]);
+ 
+     fprintf(stderr, "\n");
+     fprintf(stderr, "Performing %d user-space context switches... "
+             "be patient!\n", DO_SWITCHES);
+ 
+     stat_start = time(NULL);
+     stat_switched = 0;
+     for (i = 0; i < DO_SWITCHES; i++) {
+         stat_switched++;
+         pth_uctx_switch(uctx[0], uctx[1]);
+     }
+     stat_end = time(NULL);
+ 
+     pth_uctx_destroy(uctx[0]);
+     pth_uctx_destroy(uctx[1]);
+ 
+     fprintf(stderr, "We required %d seconds for performing the test, "
+             "so this means we can\n", (int)(stat_end-stat_start));
+     fprintf(stderr, "perform %d user-space context switches per second "
+             "on this platform.\n", DO_SWITCHES/(int)(stat_end-stat_start));
+     fprintf(stderr, "\n");
+     return;
+ }
+ 
+ int main(int argc, char *argv[])
+ {
+     test_working();
+     test_performance();
+     return 0;
+ }
+ 

CVSTrac 2.0.1