forked from pgul/binkd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
branch.c
158 lines (143 loc) · 3.24 KB
/
branch.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
* branch.c -- Create co-processes
*
* branch.c is a part of binkd project
*
* Copyright (C) 1996-1998 Dima Maloff, 5047/13
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. See COPYING.
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "sys.h"
#include "common.h"
#include "tools.h"
#include "sem.h"
#ifdef AMIGA
int ix_vfork (void);
void vfork_setup_child (void);
void ix_vfork_resume (void);
#endif
#ifdef WITH_PTHREADS
typedef struct {
void (*F) (void *);
void *args;
MUTEXSEM mutex;
#ifdef HAVE_GETTID
pid_t tid;
#endif
} thread_args_t;
static void *thread_start(void *arg)
{
void (*F) (void*);
void *args;
F = ((thread_args_t *)arg)->F;
args = ((thread_args_t *)arg)->args;
#ifdef HAVE_GETTID
((thread_args_t *)arg)->tid = PID();
#endif
ReleaseSem(&((thread_args_t *)arg)->mutex);
pthread_detach(pthread_self());
F(args);
return NULL;
}
#endif
int branch (register void (*F) (void *), register void *arg, register size_t size)
{
register int rc;
char *tmp;
/* We make our own copy of arg for the child as the parent may destroy it
* before the child finish to use it. It's not really needed with fork()
* but we do not want extra checks for HAVE_FORK before free(arg) in the
* child. */
if (size > 0)
{
if ((tmp = malloc (size)) == NULL)
{
Log (1, "malloc failed");
return -1;
}
else
{
memcpy (tmp, arg, size);
arg = tmp;
}
}
else
arg = 0;
#if defined(HAVE_FORK) && !defined(HAVE_THREADS) && !defined(AMIGA) && !defined(DEBUGCHILD)
again:
if (!(rc = fork ()))
{
/* new process */
mypid = getpid();
F (arg);
exit (0);
}
else if (rc < 0)
{
if (errno == EINTR) goto again;
/* parent, error */
Log (1, "fork: %s", strerror (errno));
}
else
{
/* parent, free our copy of args */
xfree (arg);
}
#endif
#if defined(HAVE_THREADS) && !defined(DEBUGCHILD)
#ifdef WITH_PTHREADS
{ thread_args_t args;
pthread_t tid;
args.F = F;
args.args = arg;
InitSem(&args.mutex);
LockSem(&args.mutex);
if ((rc = pthread_create (&tid, NULL, thread_start, &args)) != 0)
{
Log (1, "pthread_create: %s", strerror (rc));
rc = -1;
}
else
{
LockSem(&args.mutex); /* wait until thread releases this mutex */
#ifdef HAVE_GETTID
rc = args.tid;
#else
rc = (int)(0xffff & (long int)tid);
#endif
}
ReleaseSem(&args.mutex);
CleanSem(&args.mutex);
}
#else
if ((rc = BEGINTHREAD (F, STACKSIZE, arg)) < 0)
Log (1, "_beginthread: %s", strerror (errno));
#endif
#endif
#ifdef AMIGA
/* this is rather bizzare. this function pretends to be a fork and behaves
* like one, but actually it's a kind of a thread. so we'll need semaphores */
if (!(rc = ix_vfork ()))
{
vfork_setup_child ();
ix_vfork_resume ();
F (arg);
exit (0);
}
else if (rc < 0)
{
Log (1, "ix_vfork: %s", strerror (errno));
}
#endif
#if defined(DOS) || defined(DEBUGCHILD)
rc = 0;
F (arg);
#endif
return rc;
}