chiark / gitweb /
legal: Include DEVELOPER-CERTIFICATE
[secnet.git] / hackypar.c
1 /* Hacky parallelism */
2 /*
3  * This file is part of secnet.
4  * See README for full list of copyright holders.
5  *
6  * secnet is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  * 
11  * secnet is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License
17  * version 3 along with secnet; if not, see
18  * https://www.gnu.org/licenses/gpl.html.
19  */
20
21 #define _GNU_SOURCE
22
23 #include "secnet.h"
24 #include "util.h"
25 #include "hackypar.h"
26
27 #ifdef HACKY_PARALLEL
28
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <assert.h>
35 #include <sys/wait.h>
36
37 #define HASHSIZE 16
38 #define CACHESIZE 16
39
40 typedef enum { hp_idle, hp_compute, hp_deferring, hp_fail } HPState;
41
42 static HPState state;
43 static pid_t child;
44
45 static void checkchild(void)
46 {
47     int r, status;
48   
49     if (!child) return;
50
51     r= waitpid(child,&status,WNOHANG); if (!r) return;
52     if (r==-1) {
53         Message(M_ERR,"hacky_par: waitpid: %s\n",strerror(errno));
54         return;
55     }
56     child= 0;
57   
58     if (WIFSIGNALED(status)) {
59         Message(M_ERR,"hacky_par: signaled! %s\n",strsignal(WTERMSIG(status)));
60     } else if (!WIFEXITED(status)) {
61         Message(M_ERR,"hacky_par: unexpected status! %d\n", r);
62     }
63 }
64
65 static HPState start(void)
66 {
67     assert(!child);
68
69     child= fork();
70     if (child == -1) {
71         Message(M_ERR,"hacky_par: fork failed: %s\n",strerror(errno));
72         return hp_fail;
73     }
74
75     if (!child) { /* we are the child */
76         afterfork();
77         return hp_compute;
78     }
79
80     Message(M_INFO,"hacky_par: started, punting\n");
81     return hp_deferring;
82 }
83
84 int hacky_par_start_failnow(void)
85 {
86     state= hp_idle;
87     checkchild();
88     if (child) {
89         state= hp_deferring;
90         Message(M_INFO,"hacky_par: busy, punting\n");
91         return 1;
92     }
93     return 0;
94 }
95
96 int hacky_par_mid_failnow(void)
97 {
98     state= start();
99     return state != hp_compute;
100 }
101
102 bool_t (*packy_par_gen)(struct site *st);
103
104 void hacky_par_end(int *ok,
105                    int32_t retries, int32_t timeout,
106                    bool_t (*send_msg)(struct site *st), struct site *st)
107 {
108     int i;
109   
110     switch (state) {
111     case hp_deferring:
112         assert(!*ok);
113         *ok= 1;
114         return;
115     case hp_fail:
116         assert(!*ok);
117         return;
118     case hp_idle:
119         return;
120     case hp_compute:
121         if (!ok) {
122             Message(M_ERR,"hacky_par: compute failed\n");
123             _exit(2);
124         }
125         Message(M_INFO,"hacky_par: got result, sending\n");
126         for (i=1; i<retries; i++) {
127             sleep((timeout + 999)/1000);
128             if (!send_msg(st)) {
129                 Message(M_ERR,"hacky_par: retry failed\n");
130                 _exit(1);
131             }
132         }
133         _exit(0);
134     }
135 }
136
137 #else /*!HACKY_PARALLEL*/
138
139 int hacky_par_start_failnow(void) { return 0; }
140 int hacky_par_mid_failnow(void) { return 0; }
141 void hacky_par_end(int *ok,
142                    int32_t retries, int32_t timeout,
143                    bool_t (*send_msg)(struct site *st), struct site *st) { }
144
145 #endif /*HACKY_PARALLEL...else*/