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
;
43 struct tevent_fd
*fde
;
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 void fncall_cleanup(struct tevent_req
*req
,
126 enum tevent_req_state req_state
);
128 static bool fncall_set_pending(struct tevent_req
*req
,
129 struct fncall_context
*ctx
,
130 struct tevent_context
*ev
)
132 struct tevent_req
**pending
;
133 int num_pending
, orphaned_array_length
;
135 num_pending
= talloc_array_length(ctx
->pending
);
137 pending
= talloc_realloc(ctx
, ctx
->pending
, struct tevent_req
*,
139 if (pending
== NULL
) {
142 pending
[num_pending
] = req
;
144 ctx
->pending
= pending
;
145 tevent_req_set_cleanup_fn(req
, fncall_cleanup
);
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.
154 orphaned_array_length
= talloc_array_length(ctx
->orphaned
);
155 if (num_pending
> orphaned_array_length
) {
156 struct fncall_state
**orphaned
;
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
);
165 ctx
->orphaned
= orphaned
;
168 if (ctx
->fde
!= NULL
) {
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
);
181 static void fncall_unset_pending(struct tevent_req
*req
)
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
);
188 tevent_req_set_cleanup_fn(req
, NULL
);
190 if (num_pending
== 1) {
191 TALLOC_FREE(ctx
->fde
);
192 TALLOC_FREE(ctx
->pending
);
196 for (i
=0; i
<num_pending
; i
++) {
197 if (req
== ctx
->pending
[i
]) {
201 if (i
== num_pending
) {
204 if (num_pending
> 1) {
205 ctx
->pending
[i
] = ctx
->pending
[num_pending
-1];
207 ctx
->pending
= talloc_realloc(NULL
, ctx
->pending
, struct tevent_req
*,
211 static void fncall_cleanup(struct tevent_req
*req
,
212 enum tevent_req_state req_state
)
214 struct fncall_state
*state
= tevent_req_data(
215 req
, struct fncall_state
);
216 struct fncall_context
*ctx
= state
->ctx
;
219 case TEVENT_REQ_RECEIVED
:
225 fncall_unset_pending(req
);
232 * Keep around the state of the deleted request until the request has
233 * finished in the helper thread. fncall_handler will destroy it.
235 ctx
->orphaned
[ctx
->num_orphaned
] = talloc_move(ctx
->orphaned
, &state
);
236 ctx
->num_orphaned
+= 1;
239 struct tevent_req
*fncall_send(TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
240 struct fncall_context
*ctx
,
241 void (*fn
)(void *private_data
),
244 struct tevent_req
*req
;
245 struct fncall_state
*state
;
248 req
= tevent_req_create(mem_ctx
, &state
, struct fncall_state
);
253 state
->job_id
= fncall_next_job_id(state
->ctx
);
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.
266 state
->private_parent
= talloc_parent(private_data
);
267 state
->job_private
= talloc_move(state
, &private_data
);
269 ret
= pthreadpool_add_job(state
->ctx
->pool
, state
->job_id
, fn
,
272 tevent_req_error(req
, errno
);
273 return tevent_req_post(req
, ev
);
275 if (!fncall_set_pending(req
, state
->ctx
, ev
)) {
277 return tevent_req_post(req
, ev
);
282 static void fncall_handler(struct tevent_context
*ev
, struct tevent_fd
*fde
,
283 uint16_t flags
, void *private_data
)
285 struct fncall_context
*ctx
= talloc_get_type_abort(
286 private_data
, struct fncall_context
);
290 if (pthreadpool_finished_jobs(ctx
->pool
, &job_id
, 1) < 0) {
294 num_pending
= talloc_array_length(ctx
->pending
);
296 for (i
=0; i
<num_pending
; i
++) {
297 struct fncall_state
*state
= tevent_req_data(
298 ctx
->pending
[i
], struct fncall_state
);
300 if (job_id
== state
->job_id
) {
302 talloc_move(state
->private_parent
,
303 &state
->job_private
);
304 tevent_req_done(ctx
->pending
[i
]);
309 for (i
=0; i
<ctx
->num_orphaned
; i
++) {
310 if (job_id
== ctx
->orphaned
[i
]->job_id
) {
314 if (i
== ctx
->num_orphaned
) {
318 TALLOC_FREE(ctx
->orphaned
[i
]);
320 if (i
< ctx
->num_orphaned
-1) {
321 ctx
->orphaned
[i
] = ctx
->orphaned
[ctx
->num_orphaned
-1];
323 ctx
->num_orphaned
-= 1;
326 int fncall_recv(struct tevent_req
*req
, int *perr
)
328 if (tevent_req_is_unix_error(req
, perr
)) {