1 /*******************************************************************************
2 3 SafeFork
4 5 Offers some wrappers for the usage of fork to call expensive blocking
6 functions without interrupting the main process and without the need to
7 synchronize.
8 9 Useful version switches:
10 TimeFork = measures and displays the time taken by the linux fork() call
11 12 Copyright:
13 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
14 All rights reserved.
15 16 License:
17 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
18 Alternatively, this file may be distributed under the terms of the Tango
19 3-Clause BSD License (see LICENSE_BSD.txt for details).
20 21 *******************************************************************************/22 23 moduleocean.sys.SafeFork;
24 25 26 27 28 importocean.sys.ErrnoException;
29 30 importcore.sys.posix.stdlib : exit;
31 32 importcore.sys.posix.unistd : fork;
33 34 importocean.stdc.posix.sys.wait;
35 36 importcore.sys.posix.signal;
37 38 importcore.stdc.errno;
39 40 importcore.stdc.string;
41 42 43 44 45 version ( TimeFork )
46 {
47 importocean.io.Stdout;
48 49 importocean.time.StopWatch;
50 }
51 52 53 54 /*******************************************************************************
55 56 External C
57 58 // TODO: forced to be public to be used with reflection, must be moved to
59 C bindings
60 61 *******************************************************************************/62 63 extern (C)
64 {
65 enumidtype_t66 {
67 P_ALL, /* Wait for any child. */68 P_PID, /* Wait for specified process. */69 P_PGID/* Wait for members of process group. */70 };
71 72 intwaitid(idtype_t, id_t, siginfo_t*, int);
73 74 staticimmutableWEXITED = 0x00000004;
75 staticimmutableWNOWAIT = 0x01000000;
76 }
77 78 79 80 /*******************************************************************************
81 82 SafeFork
83 84 Offers some wrappers for the usage of fork to call expensive blocking
85 functions without interrupting the main process and without the need to
86 synchronize.
87 88 Usage Example:
89 -----
90 import ocean.sys.SafeFork;
91 92 void main ( )
93 {
94 auto dont_block = new SafeFork(&blocking_function);
95 96 dont_block.call(); // call blocking_function
97 98 if ( !dont_block.call() )
99 {
100 Stdout("blocking function is currently running and not done yet!");
101 }
102 103 while ( dont_block.isRunning() )
104 {
105 Stdout("blocking function is still running!");
106 }
107 108 if ( !dont_block.call() )
109 {
110 Stdout("blocking function is currently running and not done yet!");
111 }
112 113 dont_block.call(true); // wait for a unfinished fork and then call
114 // blocking_function without forking
115 }
116 -----
117 118 *******************************************************************************/119 120 publicclassSafeFork121 {
122 /***************************************************************************
123 124 Exception, reusable
125 126 ***************************************************************************/127 128 privateErrnoExceptionexception;
129 130 /***************************************************************************
131 132 Pid of the forked child
133 134 ***************************************************************************/135 136 privateintchild_pid = 0;
137 138 /***************************************************************************
139 140 Delegate to call
141 142 ***************************************************************************/143 144 privatevoiddelegate () dg;
145 146 /***************************************************************************
147 148 Constructor
149 150 Params:
151 dg = delegate to call
152 153 ***************************************************************************/154 155 publicthis ( scopevoiddelegate () dg )
156 {
157 this.dg = dg;
158 159 this.exception = newErrnoException;
160 }
161 162 /***************************************************************************
163 164 Find out whether the fork is still running or not
165 166 Returns:
167 true if the fork is still running, else false
168 169 ***************************************************************************/170 171 publicboolisRunning ( )
172 {
173 returnthis.child_pid == 0174 ? false175 : this.isRunning(false, false);
176 }
177 178 /***************************************************************************
179 180 Call the delegate, possibly within a fork.
181 Ensures that the delegate will only be called when there is not already
182 a fork running. The fork exits after the delegate returned.
183 184 Note that the host process is not informed about any errors in
185 the forked process.
186 187 Params:
188 block = if true, wait for a currently running fork and don't fork
189 when calling the delegate
190 if false, don't do anything when a fork is currently running
191 192 Returns:
193 true when the delegate was called
194 195 See_Also:
196 SafeFork.isRunning
197 198 ***************************************************************************/199 200 publicboolcall ( boolblock = false )
201 {
202 if ( this.child_pid == 0 || !this.isRunning(block) )
203 {
204 if ( block )
205 {
206 version ( TimeFork )
207 {
208 Stdout.formatln("Running task without forking...");
209 }
210 this.dg();
211 212 this.child_pid = 0;
213 214 returntrue;
215 }
216 else217 {
218 version ( TimeFork )
219 {
220 Stdout.formatln("Running task in fork...");
221 StopWatchsw;
222 sw.start;
223 }
224 225 this.child_pid = fork();
226 227 version ( TimeFork )
228 {
229 Stdout.formatln("Fork took {}s",
230 (cast(float)sw.microsec) / 1_000_000.0f);
231 }
232 233 this.exception.enforce(this.child_pid >= 0, "failed to fork");
234 235 if ( this.child_pid == 0 )
236 {
237 this.dg();
238 exit(0);
239 }
240 241 returntrue;
242 }
243 }
244 else245 {
246 returnfalse;
247 }
248 }
249 250 /***************************************************************************
251 252 Checks whether the forked process is already running.
253 254 Params:
255 block = if true, wait for a currently running fork
256 if false, don't do anything when a fork is currently running
257 clear = if true, the waiting status of the forked process is cleared
258 259 Returns:
260 true if the forked process is running
261 262 See_Also:
263 SafeFork.isRunning
264 265 ***************************************************************************/266 267 privateboolisRunning ( boolblock, boolclear = true )
268 {
269 if ( this.child_pid < 0 )
270 returnfalse;
271 272 siginfo_tsiginfo;
273 274 // In the case where we are blocking, we need to consider signals275 // arriving while we wait, and resume the waiting if EINTR is returned276 intwaitid_ret = void;
277 do278 {
279 waitid_ret = waitid(idtype_t.P_PID, this.child_pid, &siginfo,
280 WEXITED | (block ? 0 : WNOHANG) | (clear ? 0 : WNOWAIT));
281 }
282 while (waitid_ret == -1 && errno == EINTR);
283 284 this.exception.enforce(waitid_ret == 0, "waitid failed", "waitid");
285 286 returnsiginfo._sifields._kill.si_pid == 0;
287 }
288 }
289