lv2 rework, sams ffmpeg icons, elision patch
[goodguy/history.git] / cinelerra-5.1 / 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 int ForkChild::child_iteration()
68 {
69         int ret = read_child(100);
70         if( ret <= 0 ) return ret;
71         return handle_child();
72 }
73
74 void ForkParent::start_child()
75 {
76         lock("ForkParent::new_child");
77         int sockets[2]; // Create the process & socket pair.
78         socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
79         child_fd = sockets[0];  parent_fd = sockets[1];
80         ppid = getpid();
81         pid = fork();
82         if( !pid ) {    // child process
83                 ForkChild *child = new_fork();
84                 child->child_fd = child_fd;
85                 child->parent_fd = parent_fd;
86                 child->ppid = ppid;
87                 child->run();
88                 delete child;
89                 _exit(0);
90         }
91         unlock();
92 }
93
94 // Return -1 if the parent is dead
95 // Return  0 if timeout
96 // Return  1 if success
97 int ForkBase::read_timeout(int ms, int fd, void *data, int bytes)
98 {
99         fd_set rfds;
100         struct timeval timeout_struct;
101         int bytes_read = 0;
102         uint8_t *bp = (uint8_t *)data;
103
104         while( bytes_read < bytes ) {
105                 timeout_struct.tv_sec = ms / 1000;
106                 timeout_struct.tv_usec = (ms % 1000) * 1000;
107                 FD_ZERO(&rfds);
108                 FD_SET(fd, &rfds);
109                 int result = select(fd+1, &rfds, 0, 0, &timeout_struct);
110                 if( result < 0 ) perror("read_timeout select");
111                 if( result < 0 || !is_running() ) return -1;
112                 if( !result && !bytes_read ) return 0;
113                 int fragment = read(fd, bp + bytes_read, bytes - bytes_read);
114                 if( fragment < 0 ) perror("read_timeout read");
115                 if( fragment < 0 || !is_running() ) return -1;
116                 if( fragment > 0 ) bytes_read += fragment;
117         }
118         return 1;
119 }
120
121 // return 1 if child, or if parent && child is running
122 int ForkBase::is_running()
123 {
124         int status = 0;
125         if( pid && waitpid(pid, &status, WNOHANG) < 0 ) return 0;
126         return !pid || !kill(pid, 0) ? 1 : 0;
127 }
128
129 int ForkBase::read_parent(int ms)
130 {
131         token_bfr_t bfr;
132         int ret = read_timeout(ms, parent_fd, &bfr, sizeof(bfr));
133         if( ret > 0 ) {
134                 parent_token = bfr.token;
135                 parent_bytes = bfr.bytes;
136                 if( parent_bytes && parent_allocated < parent_bytes ) {
137                         delete [] parent_data;
138                         parent_data = new uint8_t[parent_allocated = parent_bytes];
139                 }
140                 if( parent_bytes ) {
141                         ret = read_timeout(1000, parent_fd, parent_data, parent_bytes);
142                         if( !ret ) {
143                                 printf("read_parent timeout: %d\n", parent_fd);
144                                 ret = -1;
145                         }
146                 }
147         }
148         return ret;
149 }
150
151 int ForkBase::read_child(int ms)
152 {
153         token_bfr_t bfr;
154         int ret = read_timeout(ms, child_fd, &bfr, sizeof(bfr));
155         if( ret > 0 ) {
156                 child_token = bfr.token;
157                 child_bytes = bfr.bytes;
158                 if( child_bytes && child_allocated < child_bytes ) {
159                         delete [] child_data;
160                         child_data = new uint8_t[child_allocated = child_bytes];
161                 }
162                 if( child_bytes ) {
163                         ret = read_timeout(1000, child_fd, child_data, child_bytes);
164                         if( !ret ) {
165                                 printf("read_child timeout: %d\n", child_fd);
166                                 ret = -1;
167                         }
168                 }
169         }
170         return ret;
171 }
172
173 void ForkBase::send_bfr(int fd, const void *bfr, int len)
174 {
175         int ret = 0;
176         for( int retries=10; --retries>=0 && (ret=write(fd, bfr, len)) < 0; ) {
177                 printf("send_bfr socket(%d) write error: %d/%d bytes\n%m\n", fd,ret,len);
178                 usleep(100000);
179         }
180         if( ret < len )
181                 printf("send_bfr socket(%d) write short: %d/%d bytes\n%m\n", fd,ret,len);
182 }
183
184 int ForkBase::send_parent(int64_t token, const void *data, int bytes)
185 {
186         lock("ForkBase::send_parent");
187         token_bfr_t bfr;  memset(&bfr, 0, sizeof(bfr));
188         bfr.token = token;  bfr.bytes = bytes;
189         send_bfr(child_fd, &bfr, sizeof(bfr));
190         if( data && bytes ) send_bfr(child_fd, data, bytes);
191         unlock();
192         return 0;
193 }
194
195 int ForkBase::send_child(int64_t token, const void *data, int bytes)
196 {
197         lock("ForkBase::send_child");
198         token_bfr_t bfr;  memset(&bfr, 0, sizeof(bfr));
199         bfr.token = token;  bfr.bytes = bytes;
200         send_bfr(parent_fd, &bfr, sizeof(bfr));
201         if( data && bytes ) send_bfr(parent_fd, data, bytes);
202         unlock();
203         return 0;
204 }
205
206 ForkChild::ForkChild()
207 {
208         done = 0;
209 }
210
211 ForkChild::~ForkChild()
212 {
213 }
214
215 int ForkChild::handle_child()
216 {
217         printf("ForkChild::handle_child %d\n", __LINE__);
218         return 0;
219 }
220
221 ForkParent::ForkParent()
222  : Thread(1, 0, 0)
223 {
224         done = -1;
225 }
226
227 ForkParent::~ForkParent()
228 {
229 }
230
231 // return 1 child is running
232 int ForkParent::is_running()
233 {
234         int status = 0;
235         if( waitpid(pid, &status, WNOHANG) < 0 ) return 0;
236         return !kill(pid, 0) ? 1 : 0;
237 }
238
239 // Return -1,0,1 if dead,timeout,success
240 int ForkParent::parent_iteration()
241 {
242         int ret = read_parent(100);
243         if( !ret ) return 0;
244         if( ret < 0 ) parent_token = EXIT_CODE;
245         return handle_parent();
246 }
247
248 int ForkParent::handle_parent()
249 {
250         printf("ForkParent::handle_parent %d\n", __LINE__);
251         return 0;
252 }
253
254 void ForkParent::start()
255 {
256         done = 0;
257         Thread::start();
258 }
259
260 void ForkParent::stop()
261 {
262         if( is_running() ) {
263                 send_child(EXIT_CODE, 0, 0);
264                 int retry = 10;
265                 while( --retry>=0 && is_running() ) usleep(100000);
266                 if( retry < 0 ) kill(pid, SIGKILL);
267         }
268         join();
269 }
270
271 void ForkParent::run()
272 {
273         while( !done && parent_iteration() >= 0 );
274         done = 1;
275 }
276