2 * Unix SMB/CIFS implementation.
4 * Copyright (C) Volker Lendecke 2009
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.
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.
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/>.
21 #include "../lib/util/tevent_unix.h"
23 #include "lib/pthreadpool/pthreadpool.h"
26 struct fncall_context
*ctx
;
34 struct fncall_context
{
35 struct pthreadpool
*pool
;
38 struct tevent_req
**pending
;
40 struct fncall_state
**orphaned
;
46 static void fncall_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
47 uint16_t flags
, void *private_data
);
49 static int fncall_context_destructor(struct fncall_context
*ctx
)
51 while (talloc_array_length(ctx
->pending
) != 0) {
52 /* No TALLOC_FREE here */
53 talloc_free(ctx
->pending
[0]);
56 while (ctx
->num_orphaned
!= 0) {
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.
61 fncall_handler(NULL
, NULL
, TEVENT_FD_READ
, ctx
);
64 pthreadpool_destroy(ctx
->pool
);
70 struct fncall_context
*fncall_context_init(TALLOC_CTX
*mem_ctx
,
73 struct fncall_context
*ctx
;
76 ctx
= talloc_zero(mem_ctx
, struct fncall_context
);
81 ret
= pthreadpool_init(max_threads
, &ctx
->pool
);
86 talloc_set_destructor(ctx
, fncall_context_destructor
);
88 ctx
->sig_fd
= pthreadpool_signal_fd(ctx
->pool
);
89 if (ctx
->sig_fd
== -1) {
97 static int fncall_next_job_id(struct fncall_context
*ctx
)
99 int num_pending
= talloc_array_length(ctx
->pending
);
105 result
= ctx
->next_job_id
++;
110 for (i
=0; i
<num_pending
; i
++) {
111 struct fncall_state
*state
= tevent_req_data(
112 ctx
->pending
[i
], struct fncall_state
);
114 if (result
== state
->job_id
) {
118 if (i
== num_pending
) {
124 static void fncall_unset_pending(struct tevent_req
*req
);
125 static int fncall_destructor(struct tevent_req
*req
);
127 static bool fncall_set_pending(struct tevent_req
*req
,
128 struct fncall_context
*ctx
,
129 struct tevent_context
*ev
)
131 struct tevent_req
**pending
;
132 int num_pending
, orphaned_array_length
;
134 num_pending
= talloc_array_length(ctx
->pending
);
136 pending
= talloc_realloc(ctx
, ctx
->pending
, struct tevent_req
*,
138 if (pending
== NULL
) {
141 pending
[num_pending
] = req
;
143 ctx
->pending
= pending
;
144 talloc_set_destructor(req
, fncall_destructor
);
147 * Make sure that the orphaned array of fncall_state structs has
148 * enough space. A job can change from pending to orphaned in
149 * fncall_destructor, and to fail in a talloc destructor should be
150 * avoided if possible.
153 orphaned_array_length
= talloc_array_length(ctx
->orphaned
);
154 if (num_pending
> orphaned_array_length
) {
155 struct fncall_state
**orphaned
;
157 orphaned
= talloc_realloc(ctx
, ctx
->orphaned
,
158 struct fncall_state
*,
159 orphaned_array_length
+ 1);
160 if (orphaned
== NULL
) {
161 fncall_unset_pending(req
);
164 ctx
->orphaned
= orphaned
;
167 if (ctx
->fde
!= NULL
) {
171 ctx
->fde
= tevent_add_fd(ev
, ctx
->pending
, ctx
->sig_fd
, TEVENT_FD_READ
,
172 fncall_handler
, ctx
);
173 if (ctx
->fde
== NULL
) {
174 fncall_unset_pending(req
);
180 static void fncall_unset_pending(struct tevent_req
*req
)
182 struct fncall_state
*state
= tevent_req_data(req
, struct fncall_state
);
183 struct fncall_context
*ctx
= state
->ctx
;
184 int num_pending
= talloc_array_length(ctx
->pending
);
187 if (num_pending
== 1) {
188 TALLOC_FREE(ctx
->fde
);
189 TALLOC_FREE(ctx
->pending
);
193 for (i
=0; i
<num_pending
; i
++) {
194 if (req
== ctx
->pending
[i
]) {
198 if (i
== num_pending
) {
201 if (num_pending
> 1) {
202 ctx
->pending
[i
] = ctx
->pending
[num_pending
-1];
204 ctx
->pending
= talloc_realloc(NULL
, ctx
->pending
, struct tevent_req
*,
208 static int fncall_destructor(struct tevent_req
*req
)
210 struct fncall_state
*state
= tevent_req_data(
211 req
, struct fncall_state
);
212 struct fncall_context
*ctx
= state
->ctx
;
214 fncall_unset_pending(req
);
221 * Keep around the state of the deleted request until the request has
222 * finished in the helper thread. fncall_handler will destroy it.
224 ctx
->orphaned
[ctx
->num_orphaned
] = talloc_move(ctx
->orphaned
, &state
);
225 ctx
->num_orphaned
+= 1;
230 struct tevent_req
*fncall_send(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
231 struct fncall_context
*ctx
,
232 void (*fn
)(void *private_data
),
235 struct tevent_req
*req
;
236 struct fncall_state
*state
;
239 req
= tevent_req_create(mem_ctx
, &state
, struct fncall_state
);
244 state
->job_id
= fncall_next_job_id(state
->ctx
);
248 * We need to keep the private data we handed out to the thread around
249 * as long as the job is not finished. This is a bit of an abstraction
250 * violation, because the "req->state1->subreq->state2" (we're
251 * "subreq", "req" is the request our caller creates) is broken to
252 * "ctx->state2->state1", but we are right now in the destructor for
253 * "subreq2", so what can we do. We need to keep state1 around,
254 * otherwise the helper thread will have no place to put its results.
257 state
->private_parent
= talloc_parent(private_data
);
258 state
->job_private
= talloc_move(state
, &private_data
);
260 ret
= pthreadpool_add_job(state
->ctx
->pool
, state
->job_id
, fn
,
263 tevent_req_error(req
, errno
);
264 return tevent_req_post(req
, ev
);
266 if (!fncall_set_pending(req
, state
->ctx
, ev
)) {
268 return tevent_req_post(req
, ev
);
273 static void fncall_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
274 uint16_t flags
, void *private_data
)
276 struct fncall_context
*ctx
= talloc_get_type_abort(
277 private_data
, struct fncall_context
);
281 if (pthreadpool_finished_job(ctx
->pool
, &job_id
) != 0) {
285 num_pending
= talloc_array_length(ctx
->pending
);
287 for (i
=0; i
<num_pending
; i
++) {
288 struct fncall_state
*state
= tevent_req_data(
289 ctx
->pending
[i
], struct fncall_state
);
291 if (job_id
== state
->job_id
) {
293 talloc_move(state
->private_parent
,
294 &state
->job_private
);
295 tevent_req_done(ctx
->pending
[i
]);
300 for (i
=0; i
<ctx
->num_orphaned
; i
++) {
301 if (job_id
== ctx
->orphaned
[i
]->job_id
) {
305 if (i
== ctx
->num_orphaned
) {
309 TALLOC_FREE(ctx
->orphaned
[i
]);
311 if (i
< ctx
->num_orphaned
-1) {
312 ctx
->orphaned
[i
] = ctx
->orphaned
[ctx
->num_orphaned
-1];
314 ctx
->num_orphaned
-= 1;
317 int fncall_recv(struct tevent_req
*req
, int *perr
)
319 if (tevent_req_is_unix_error(req
, perr
)) {