source: vendor/current/source3/lib/fncall.c

Last change on this file was 988, checked in by Silvan Scherrer, 9 years ago

Samba Server: update vendor to version 4.4.3

File size: 7.8 KB
Line 
1/*
2 * Unix SMB/CIFS implementation.
3 * Async fn calls
4 * Copyright (C) Volker Lendecke 2009
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 3 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, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "includes.h"
21#include "../lib/util/tevent_unix.h"
22
23#include "lib/pthreadpool/pthreadpool.h"
24
25struct fncall_state {
26 struct fncall_context *ctx;
27 int job_id;
28 bool done;
29
30 void *private_parent;
31 void *job_private;
32};
33
34struct fncall_context {
35 struct pthreadpool *pool;
36 int next_job_id;
37 int sig_fd;
38 struct tevent_req **pending;
39
40 struct fncall_state **orphaned;
41 int num_orphaned;
42
43 struct tevent_fd *fde;
44};
45
46static void fncall_handler(struct tevent_context *ev, struct tevent_fd *fde,
47 uint16_t flags, void *private_data);
48
49static int fncall_context_destructor(struct fncall_context *ctx)
50{
51 while (talloc_array_length(ctx->pending) != 0) {
52 /* No TALLOC_FREE here */
53 talloc_free(ctx->pending[0]);
54 }
55
56 while (ctx->num_orphaned != 0) {
57 /*
58 * We've got jobs in the queue for which the tevent_req has
59 * been finished already. Wait for all of them to finish.
60 */
61 fncall_handler(NULL, NULL, TEVENT_FD_READ, ctx);
62 }
63
64 pthreadpool_destroy(ctx->pool);
65 ctx->pool = NULL;
66
67 return 0;
68}
69
70struct fncall_context *fncall_context_init(TALLOC_CTX *mem_ctx,
71 int max_threads)
72{
73 struct fncall_context *ctx;
74 int ret;
75
76 ctx = talloc_zero(mem_ctx, struct fncall_context);
77 if (ctx == NULL) {
78 return NULL;
79 }
80
81 ret = pthreadpool_init(max_threads, &ctx->pool);
82 if (ret != 0) {
83 TALLOC_FREE(ctx);
84 return NULL;
85 }
86 talloc_set_destructor(ctx, fncall_context_destructor);
87
88 ctx->sig_fd = pthreadpool_signal_fd(ctx->pool);
89 if (ctx->sig_fd == -1) {
90 TALLOC_FREE(ctx);
91 return NULL;
92 }
93
94 return ctx;
95}
96
97static int fncall_next_job_id(struct fncall_context *ctx)
98{
99 int num_pending = talloc_array_length(ctx->pending);
100 int result;
101
102 while (true) {
103 int i;
104
105 result = ctx->next_job_id++;
106 if (result == 0) {
107 continue;
108 }
109
110 for (i=0; i<num_pending; i++) {
111 struct fncall_state *state = tevent_req_data(
112 ctx->pending[i], struct fncall_state);
113
114 if (result == state->job_id) {
115 break;
116 }
117 }
118 if (i == num_pending) {
119 return result;
120 }
121 }
122}
123
124static void fncall_unset_pending(struct tevent_req *req);
125static void fncall_cleanup(struct tevent_req *req,
126 enum tevent_req_state req_state);
127
128static bool fncall_set_pending(struct tevent_req *req,
129 struct fncall_context *ctx,
130 struct tevent_context *ev)
131{
132 struct tevent_req **pending;
133 int num_pending, orphaned_array_length;
134
135 num_pending = talloc_array_length(ctx->pending);
136
137 pending = talloc_realloc(ctx, ctx->pending, struct tevent_req *,
138 num_pending+1);
139 if (pending == NULL) {
140 return false;
141 }
142 pending[num_pending] = req;
143 num_pending += 1;
144 ctx->pending = pending;
145 tevent_req_set_cleanup_fn(req, fncall_cleanup);
146
147 /*
148 * Make sure that the orphaned array of fncall_state structs has
149 * enough space. A job can change from pending to orphaned in
150 * fncall_cleanup, and to fail in a talloc destructor should be
151 * avoided if possible.
152 */
153
154 orphaned_array_length = talloc_array_length(ctx->orphaned);
155 if (num_pending > orphaned_array_length) {
156 struct fncall_state **orphaned;
157
158 orphaned = talloc_realloc(ctx, ctx->orphaned,
159 struct fncall_state *,
160 orphaned_array_length + 1);
161 if (orphaned == NULL) {
162 fncall_unset_pending(req);
163 return false;
164 }
165 ctx->orphaned = orphaned;
166 }
167
168 if (ctx->fde != NULL) {
169 return true;
170 }
171
172 ctx->fde = tevent_add_fd(ev, ctx->pending, ctx->sig_fd, TEVENT_FD_READ,
173 fncall_handler, ctx);
174 if (ctx->fde == NULL) {
175 fncall_unset_pending(req);
176 return false;
177 }
178 return true;
179}
180
181static void fncall_unset_pending(struct tevent_req *req)
182{
183 struct fncall_state *state = tevent_req_data(req, struct fncall_state);
184 struct fncall_context *ctx = state->ctx;
185 int num_pending = talloc_array_length(ctx->pending);
186 int i;
187
188 tevent_req_set_cleanup_fn(req, NULL);
189
190 if (num_pending == 1) {
191 TALLOC_FREE(ctx->fde);
192 TALLOC_FREE(ctx->pending);
193 return;
194 }
195
196 for (i=0; i<num_pending; i++) {
197 if (req == ctx->pending[i]) {
198 break;
199 }
200 }
201 if (i == num_pending) {
202 return;
203 }
204 if (num_pending > 1) {
205 ctx->pending[i] = ctx->pending[num_pending-1];
206 }
207 ctx->pending = talloc_realloc(NULL, ctx->pending, struct tevent_req *,
208 num_pending - 1);
209}
210
211static void fncall_cleanup(struct tevent_req *req,
212 enum tevent_req_state req_state)
213{
214 struct fncall_state *state = tevent_req_data(
215 req, struct fncall_state);
216 struct fncall_context *ctx = state->ctx;
217
218 switch (req_state) {
219 case TEVENT_REQ_RECEIVED:
220 break;
221 default:
222 return;
223 }
224
225 fncall_unset_pending(req);
226
227 if (state->done) {
228 return;
229 }
230
231 /*
232 * Keep around the state of the deleted request until the request has
233 * finished in the helper thread. fncall_handler will destroy it.
234 */
235 ctx->orphaned[ctx->num_orphaned] = talloc_move(ctx->orphaned, &state);
236 ctx->num_orphaned += 1;
237}
238
239struct tevent_req *fncall_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
240 struct fncall_context *ctx,
241 void (*fn)(void *private_data),
242 void *private_data)
243{
244 struct tevent_req *req;
245 struct fncall_state *state;
246 int ret;
247
248 req = tevent_req_create(mem_ctx, &state, struct fncall_state);
249 if (req == NULL) {
250 return NULL;
251 }
252 state->ctx = ctx;
253 state->job_id = fncall_next_job_id(state->ctx);
254 state->done = false;
255
256 /*
257 * We need to keep the private data we handed out to the thread around
258 * as long as the job is not finished. This is a bit of an abstraction
259 * violation, because the "req->state1->subreq->state2" (we're
260 * "subreq", "req" is the request our caller creates) is broken to
261 * "ctx->state2->state1", but we are right now in the destructor for
262 * "subreq2", so what can we do. We need to keep state1 around,
263 * otherwise the helper thread will have no place to put its results.
264 */
265
266 state->private_parent = talloc_parent(private_data);
267 state->job_private = talloc_move(state, &private_data);
268
269 ret = pthreadpool_add_job(state->ctx->pool, state->job_id, fn,
270 state->job_private);
271 if (ret == -1) {
272 tevent_req_error(req, errno);
273 return tevent_req_post(req, ev);
274 }
275 if (!fncall_set_pending(req, state->ctx, ev)) {
276 tevent_req_oom(req);
277 return tevent_req_post(req, ev);
278 }
279 return req;
280}
281
282static void fncall_handler(struct tevent_context *ev, struct tevent_fd *fde,
283 uint16_t flags, void *private_data)
284{
285 struct fncall_context *ctx = talloc_get_type_abort(
286 private_data, struct fncall_context);
287 int i, num_pending;
288 int job_id;
289
290 if (pthreadpool_finished_jobs(ctx->pool, &job_id, 1) < 0) {
291 return;
292 }
293
294 num_pending = talloc_array_length(ctx->pending);
295
296 for (i=0; i<num_pending; i++) {
297 struct fncall_state *state = tevent_req_data(
298 ctx->pending[i], struct fncall_state);
299
300 if (job_id == state->job_id) {
301 state->done = true;
302 talloc_move(state->private_parent,
303 &state->job_private);
304 tevent_req_done(ctx->pending[i]);
305 return;
306 }
307 }
308
309 for (i=0; i<ctx->num_orphaned; i++) {
310 if (job_id == ctx->orphaned[i]->job_id) {
311 break;
312 }
313 }
314 if (i == ctx->num_orphaned) {
315 return;
316 }
317
318 TALLOC_FREE(ctx->orphaned[i]);
319
320 if (i < ctx->num_orphaned-1) {
321 ctx->orphaned[i] = ctx->orphaned[ctx->num_orphaned-1];
322 }
323 ctx->num_orphaned -= 1;
324}
325
326int fncall_recv(struct tevent_req *req, int *perr)
327{
328 if (tevent_req_is_unix_error(req, perr)) {
329 return -1;
330 }
331 return 0;
332}
Note: See TracBrowser for help on using the repository browser.