Credit Andrew - improve in-tree documentation
[goodguy/cinelerra.git] / cinelerra / forkbase.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
5  * 
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  * 
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  * 
20  */
21
22 #include "bcsignals.h"
23 #include "forkbase.h"
24 #include "mwindow.h"
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <sys/wait.h>
32 #include <unistd.h>
33
34 ForkBase::ForkBase()
35  : Mutex("ForkBase::lock")
36 {
37         ppid = pid = 0;
38         child = 0;
39
40         child_fd = -1;
41         child_token = 0;
42         child_bytes = 0;
43         child_allocated = 0;
44         child_data = 0;
45
46         parent_fd = -1;
47         parent_token = 0;
48         parent_bytes = 0;
49         parent_allocated = 0;
50         parent_data = 0;
51 }
52
53 ForkBase::~ForkBase()
54 {
55         delete [] child_data;
56         delete [] parent_data;
57         if( child_fd >= 0 ) close(child_fd);
58         if( parent_fd >= 0 ) close(parent_fd);
59 }
60
61 // return 1 parent is running
62 int ForkChild::is_running()
63 {
64         return !ppid || !kill(ppid, 0) ? 1 : 0;
65 }
66
67 void ForkParent::start_child()
68 {
69         lock("ForkParent::new_child");
70         int sockets[2]; // Create the process & socket pair.
71         socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
72         child_fd = sockets[0];  parent_fd = sockets[1];
73         ppid = getpid();
74         pid = fork();
75         if( !pid ) {    // child process
76                 ForkChild *child = new_fork();
77                 child->child_fd = child_fd;
78                 child->parent_fd = parent_fd;
79                 child->ppid = ppid;
80                 child->run();
81                 delete child;
82                 _exit(0);
83         }
84         unlock();
85 }
86
87 // Return -1 if the parent is dead
88 // Return  0 if timeout
89 // Return  1 if success
90 int ForkBase::read_timeout(int64_t usec, int fd, void *data, int bytes)
91 {
92         fd_set rfds;
93         struct timeval timeout_struct;
94         int bytes_read = 0;
95         uint8_t *bp = (uint8_t *)data;
96
97         while( bytes_read < bytes ) {
98                 timeout_struct.tv_sec = usec / 1000000;
99                 timeout_struct.tv_usec = usec % 1000000;
100                 FD_ZERO(&rfds);
101                 FD_SET(fd, &rfds);
102                 int result = select(fd+1, &rfds, 0, 0, &timeout_struct);
103                 if( result < 0 ) perror("read_timeout select");
104                 if( result < 0 || !is_running() ) return -1;
105                 if( !result && !bytes_read ) return 0;
106                 int fragment = read(fd, bp + bytes_read, bytes - bytes_read);
107                 if( fragment < 0 ) perror("read_timeout read");
108                 if( fragment < 0 || !is_running() ) return -1;
109                 if( fragment > 0 ) bytes_read += fragment;
110         }
111         return 1;
112 }
113
114 // return 1 if child, or if parent && child is running
115 int ForkBase::is_running()
116 {
117         int status = 0;
118         if( pid && waitpid(pid, &status, WNOHANG) < 0 ) return 0;
119         return !pid || !kill(pid, 0) ? 1 : 0;
120 }
121
122 int ForkBase::read_parent(int64_t usec)
123 {
124         token_bfr_t bfr;
125         int ret = read_timeout(usec, parent_fd, &bfr, sizeof(bfr));
126         if( ret > 0 ) {
127                 parent_token = bfr.token;
128                 parent_bytes = bfr.bytes;
129                 if( parent_bytes && parent_allocated < parent_bytes ) {
130                         delete [] parent_data;
131                         parent_data = new uint8_t[parent_allocated = parent_bytes];
132                 }
133                 if( parent_bytes ) {
134                         ret = read_timeout(1000000, parent_fd, parent_data, parent_bytes);
135                         if( !ret ) {
136                                 printf("read_parent timeout: %d\n", parent_fd);
137                                 ret = -1;
138                         }
139                 }
140         }
141         return ret;
142 }
143
144 int ForkBase::read_child(int64_t usec)
145 {
146         token_bfr_t bfr;
147         int ret = read_timeout(usec, child_fd, &bfr, sizeof(bfr));
148         if( ret > 0 ) {
149                 child_token = bfr.token;
150                 child_bytes = bfr.bytes;
151                 if( child_bytes && child_allocated < child_bytes ) {
152                         delete [] child_data;
153                         child_data = new uint8_t[child_allocated = child_bytes];
154                 }
155                 if( child_bytes ) {
156                         ret = read_timeout(1000000, child_fd, child_data, child_bytes);
157                         if( !ret ) {
158                                 printf("read_child timeout: %d\n", child_fd);
159                                 ret = -1;
160                         }
161                 }
162         }
163         return ret;
164 }
165
166 void ForkBase::send_bfr(int fd, const void *bfr, int len)
167 {
168         int ret = 0;
169         for( int retries=10; --retries>=0 && (ret=write(fd, bfr, len)) < 0; ) {
170                 printf("send_bfr socket(%d) write error: %d/%d bytes\n%m\n", fd,ret,len);
171                 usleep(100000);
172         }
173         if( ret < len )
174                 printf("send_bfr socket(%d) write short: %d/%d bytes\n%m\n", fd,ret,len);
175 }
176
177 int ForkBase::send_parent(int64_t token, const void *data, int bytes)
178 {
179         lock("ForkBase::send_parent");
180         token_bfr_t bfr;  memset(&bfr, 0, sizeof(bfr));
181         bfr.token = token;  bfr.bytes = bytes;
182         send_bfr(child_fd, &bfr, sizeof(bfr));
183         if( data && bytes ) send_bfr(child_fd, data, bytes);
184         unlock();
185         return 0;
186 }
187
188 int ForkBase::send_child(int64_t token, const void *data, int bytes)
189 {
190         lock("ForkBase::send_child");
191         token_bfr_t bfr;  memset(&bfr, 0, sizeof(bfr));
192         bfr.token = token;  bfr.bytes = bytes;
193         send_bfr(parent_fd, &bfr, sizeof(bfr));
194         if( data && bytes ) send_bfr(parent_fd, data, bytes);
195         unlock();
196         return 0;
197 }
198
199 ForkChild::ForkChild()
200 {
201         parent_done = 0;
202 }
203
204 ForkChild::~ForkChild()
205 {
206 }
207
208 ForkParent::ForkParent()
209  : Thread(1, 0, 0)
210 {
211         parent_done = -1;
212 }
213
214 ForkParent::~ForkParent()
215 {
216 }
217
218 // return 1 child is running
219 int ForkParent::is_running()
220 {
221         int status = 0;
222         if( waitpid(pid, &status, WNOHANG) < 0 ) return 0;
223         return !kill(pid, 0) ? 1 : 0;
224 }
225
226 // Return -1,0,1 if dead,timeout,success
227 int ForkParent::parent_iteration()
228 {
229         int ret = read_parent(100);
230         if( !ret ) return 0;
231         if( ret < 0 ) parent_token = EXIT_CODE;
232         return handle_parent();
233 }
234
235 int ForkParent::handle_parent()
236 {
237         printf("ForkParent::handle_parent %d\n", __LINE__);
238         return 0;
239 }
240
241 void ForkParent::start()
242 {
243         parent_done = 0;
244         Thread::start();
245 }
246
247 void ForkParent::stop()
248 {
249         if( is_running() ) {
250                 send_child(EXIT_CODE, 0, 0);
251                 int retry = 10;
252                 while( --retry>=0 && is_running() ) usleep(100000);
253                 if( retry < 0 ) kill(pid, SIGKILL);
254         }
255         join();
256 }
257
258 void ForkParent::run()
259 {
260         while( !parent_done && parent_iteration() >= 0 );
261         parent_done = 1;
262 }
263