

// From J. P. Mellor


#ifdef JLLIB_LINUX


#include "clone.h"


/*
 * This is a "kind-of" thr_create() as in pthreads, but not really.
 * It needs some fleshing out to work like pthreads thr_create().
 */
int
sproc(void (*fn)(void *), unsigned int flags, void *dummy)
{
  long retval;
  void **newstack;

  /*
   * allocate new stack for subthread
   */
  if ((newstack=(void **)malloc(STACKSIZE)) == NULL) {
    return -1;
  }

  /*
   * Set up the stack for child function, put the (void *)
   * argument on the stack.
   */
  newstack = (void **)(STACKSIZE +(char *) newstack);
  *--newstack = dummy;

  /*
   * Do clone() system call. We need to do the low-level stuff
   * entirely in assembly as we're returning with a different
   * stack in the child process and we couldn't otherwise guarantee
   * that the program doesn't use the old stack incorrectly.
   *
   * Parameters to clone() system call:
   *	%eax - __NR_clone, clone system call number
   *	%ebx - clone_flags, bitmap of cloned data
   *	%ecx - new stack pointer for cloned child
   *
   * In this example %ebx is CLONE_VM | CLONE_FS | CLONE_FILES |
   * CLONE_SIGHAND which shares as much as possible between parent
   * and child. (We or in the signal to be sent on child termination
   * into clone_flags: SIGCHLD makes the cloned process work like
   * a "normal" unix child process)
   *
   * The clone() system call returns (in %eax) the pid of the newly
   * cloned process to the parent, and 0 to the cloned process. If
   * an error occurs, the return value will be the negative errno.
   *
   * In the child process, we will do a "jsr" to the requested function
   * and then do a "exit()" system call which will terminate the child.
   */
  __asm__ __volatile__(
		       "int $0x80\n\t"	/* Linux/i386 system call */
		       "testl %0,%0\n\t"	/* check return value */
		       "jne 1f\n\t"	/* jump if parent */
		       "call *%3\n\t"	/* start subthread function */
		       "movl %2,%0\n\t"
		       "int $0x80\n"	/* exit system call: exit subthread */
		       "1:\t"
		       :"=a" (retval)
		       :"0" (__NR_clone),"i" (__NR_exit),
		       "r" (fn),
		       "b" (flags),
		       "c" (newstack));

  if (retval < 0) {
    errno = -retval;
    retval = -1;
  }
  return retval;
}

#endif
