home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / gnulib / libsrc98.zoo / fork.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-09-24  |  5.3 KB  |  215 lines

  1. /*
  2.  * _fork(), _wait(): create a new process, and wait for it to finish.
  3.  * the parameter to _fork() controls the way memory is shared with the
  4.  * parent; 0 means share all data and stack, non-zero means just share
  5.  * stack up to the address given. Note that _fork(0) is callable only
  6.  * when malloc's are being done from the heap. If _heapbase is
  7.  * non-zero, _fork(0) fails and errno is set to EACCESS.
  8.  *
  9.  * written by Eric R. Smith and placed in the public domain.
  10.  * use at your own risk.
  11.  */
  12.  
  13. #include <stdlib.h>
  14. #include <stddef.h>        /* for size_t */
  15. #include <osbind.h>
  16. #include <basepage.h>
  17. #include <errno.h>
  18. #include <setjmp.h>
  19. #include "fork.h"
  20.  
  21. struct _fork_block *_forks = 0;
  22. extern long _stksize;
  23.  
  24. static short wait_return;
  25.  
  26. /* newly forked processes start here */
  27. /* all we do is set up a temporary stack and longjmp() back to _fork() */
  28.  
  29. static void
  30. in_fork(bp)
  31. long bp;
  32. {
  33.     __asm__ volatile("movl %0,sp"::"r"(bp+500));
  34.     longjmp(_forks->ctxt, 1);
  35. }
  36.  
  37. /*
  38.  * the guts of fork() and vfork(). _fork(0) is the same as fork();
  39.  * _fork(x) gives a vfork(), and in this case "x" is the top address
  40.  * we should save to. note that the parent is suspended until the child
  41.  * exits (this is unlike Unix). also note that _fork(0) requires that
  42.  * _heapbase be non-zero, i.e. that mallocs be done from the heap.
  43.  * otherwise, we would have to keep track of all memory requests from
  44.  * the system, and somehow put them all into the saved block. this is
  45.  * doable, but would be a pain.
  46.  */
  47.  
  48. int
  49. _fork(save_to)
  50. char *save_to;
  51. {
  52.     struct _fork_block *new;
  53.     char *cpystart;
  54.     long siz, *to, *from;
  55.     long r;
  56.     BASEPAGE *newbase;
  57.     extern void *_heapbase;
  58.  
  59.     if (!save_to) {
  60. /* fork() requires stack based memory allocation */
  61.         if (!_heapbase) {
  62.             errno = EACCESS;
  63.             return -1;
  64.         }
  65.         siz = ((char *)_heapbase - _base->p_dbase) + _stksize;
  66.         cpystart = (char *)_base->p_dbase;
  67.     }
  68.     else {
  69. /*
  70.  * for vfork, start copying just below where the stack is now, and
  71.  * copy up to just past save_to, leaving some slush on either
  72.  * side for return addresses, linkages, and saved regs
  73.  */
  74.         cpystart = ((char *)&save_to) - 128;
  75.         siz = (save_to - cpystart) + 72;
  76.     }
  77.  
  78. /*
  79.  * KLUDGE!
  80.  * make sure that there is enough free memory handled by malloc, so that
  81.  * it doesn't have to get any more from TOS if the "child" process
  82.  * does a malloc() before it does an execve() (otherwise malloc() could
  83.  * later return invalid memory blocks!) ++andreas
  84.  */
  85.     free(malloc((size_t)16*1024)); /* hope this is enough! */
  86.  
  87. /* now malloc a _fork_block to describe this fork */
  88.     if (!(new = (void *)Malloc(siz+sizeof(struct _fork_block)))) {
  89.         errno = ENOMEM;
  90.         return -1;
  91.     }
  92.  
  93.  
  94. /* adjust size, since pointers are (long *) */
  95.     siz = siz/sizeof(long);
  96.  
  97. /* link the new fork block into the list and fill in some entries */
  98.     new->next = _forks;
  99.     new->start_addr = (long *)cpystart;
  100.     new->data_size = siz;
  101.     new->ppid = _base;
  102.     _forks = new;
  103.  
  104. /* make a new basepage for the child process */
  105.  
  106.     newbase = (BASEPAGE *)Pexec(PE_CBASEPAGE, 0L, "", 0L);
  107.     if ((long)newbase < 0) {
  108.         errno =  -((int)newbase);
  109.         _forks = _forks->next;
  110.         Mfree(new);
  111.         return -1;
  112.     }
  113.     new->pid = newbase;
  114.  
  115. /* save the necessary data */
  116.     to = new->data;
  117.     from = (long *)cpystart;
  118.     r = siz;
  119.     while (r-- > 0)
  120.         *to++ = *from++;
  121.  
  122. /*
  123.    make the new basepage just like ours, so that the child will be able to
  124.    fork() too
  125. */
  126.     newbase->p_dbase = _base->p_dbase;
  127.     newbase->p_bbase = _base->p_bbase;
  128.     newbase->p_dlen = _base->p_dlen;
  129.     newbase->p_blen = _base->p_blen;
  130.     newbase->p_tbase = (char *)in_fork;
  131.     newbase->p_tlen = 0;
  132.  
  133.     if (r = setjmp(new->ctxt)) {
  134. /* the second return is caused by the child, from in_fork() */
  135.         Mshrink(_base, 256L);
  136.         return 0;
  137.     }
  138.     else {
  139. /* If the child stomps on the parent's stack, then Pexec won't return to
  140.  * the right place. So we set to a temporary stack before the Pexec.
  141.  */
  142.         _base = newbase;
  143.         r = (long)&(new->tmp_stack[62]);
  144.         __asm__ volatile
  145.         ("
  146.             movel sp, a0;
  147.             movel %0, sp;
  148.             movel a0, sp@- "
  149.         :
  150.         : "r"(r)
  151.         : "a0", "sp"
  152.         );
  153.         wait_return = Pexec(PE_GO, 0L, newbase, 0L);
  154.         __asm__ volatile
  155.         ("
  156.             movel sp@+, a0;
  157.             movel a0, sp "
  158.         ::: "a0", "sp"
  159.         );
  160.  
  161. /* we're back in the parent here, so restore everything */
  162.         new = *((struct _fork_block * volatile *)&_forks);
  163.         _base = new->ppid;
  164.         new->return_code = wait_return;
  165. /* restore data */
  166.         to = new->start_addr;
  167.         from = new->data;
  168.         siz = new->data_size;
  169.         while (siz-- > 0)
  170.             *to++ = *from++;
  171.         Mfree(newbase->p_env);
  172.         Mfree(newbase);
  173. /* shrink the fork block to the minimum size (it can't be freed yet,
  174.  * because wait() may need it)
  175.  */
  176.         Mshrink(new, sizeof(struct _fork_block));
  177.         return BP_TO_PID(newbase);
  178.     }
  179. }
  180.  
  181. /*
  182.  * wait() emulation; goes through the fork block structure and returns
  183.  * the pid of a child we haven't waited for yet. if such a child exists,
  184.  * and exit_code is non-zero, the integer pointed at by exit_code is
  185.  * set to the child's exit status. if no child is found, -1 is returned
  186.  * and errno is set.
  187.  */
  188.  
  189. int _wait(exit_code)
  190.     int *exit_code;
  191. {
  192.     struct _fork_block *f, *g;
  193.     int r;
  194.  
  195.     g = 0;
  196.     f = _forks;
  197.     while (f) {
  198.         if (f->ppid == _base) {    /* is this one our child?? */
  199.             r = BP_TO_PID(f->pid);
  200.             if (exit_code)
  201.                 *exit_code = f->return_code;
  202.             if (g)
  203.                 g->next = f->next;
  204.             else
  205.                 _forks = f->next;
  206.             Mfree(f);
  207.             return r;
  208.         }
  209.         g = f;
  210.         f = f->next;
  211.     }
  212.     errno = ENMFILES;    /* FIX_ME:: should be ESRCH */
  213.     return -1;
  214. }
  215.