]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | /* | |
4 | * Ceph - scalable distributed file system | |
5 | * | |
6 | * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net> | |
7 | * | |
8 | * This is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU Lesser General Public | |
10 | * License version 2.1, as published by the Free Software | |
11 | * Foundation. See file COPYING. | |
12 | * | |
13 | */ | |
14 | ||
15 | ||
16 | #ifndef CEPH_CONTEXT_H | |
17 | #define CEPH_CONTEXT_H | |
18 | ||
19 | #include "common/dout.h" | |
20 | ||
21 | #include <boost/function.hpp> | |
22 | #include <list> | |
23 | #include <set> | |
24 | #include <memory> | |
25 | ||
11fdf7f2 TL |
26 | #include "include/ceph_assert.h" |
27 | #include "common/Mutex.h" | |
7c673cae FG |
28 | |
29 | #define mydout(cct, v) lgeneric_subdout(cct, context, v) | |
30 | ||
31 | /* | |
32 | * GenContext - abstract callback class | |
33 | */ | |
34 | template <typename T> | |
35 | class GenContext { | |
36 | GenContext(const GenContext& other); | |
37 | const GenContext& operator=(const GenContext& other); | |
38 | ||
39 | protected: | |
40 | virtual void finish(T t) = 0; | |
41 | ||
42 | public: | |
43 | GenContext() {} | |
44 | virtual ~GenContext() {} // we want a virtual destructor!!! | |
45 | ||
46 | template <typename C> | |
47 | void complete(C &&t) { | |
48 | finish(std::forward<C>(t)); | |
49 | delete this; | |
50 | } | |
51 | }; | |
52 | ||
53 | template <typename T> | |
54 | using GenContextURef = std::unique_ptr<GenContext<T> >; | |
55 | ||
56 | /* | |
57 | * Context - abstract callback class | |
58 | */ | |
11fdf7f2 | 59 | class Finisher; |
7c673cae FG |
60 | class Context { |
61 | Context(const Context& other); | |
62 | const Context& operator=(const Context& other); | |
63 | ||
64 | protected: | |
65 | virtual void finish(int r) = 0; | |
66 | ||
11fdf7f2 TL |
67 | // variant of finish that is safe to call "synchronously." override should |
68 | // return true. | |
69 | virtual bool sync_finish(int r) { | |
70 | return false; | |
71 | } | |
72 | ||
7c673cae FG |
73 | public: |
74 | Context() {} | |
75 | virtual ~Context() {} // we want a virtual destructor!!! | |
76 | virtual void complete(int r) { | |
77 | finish(r); | |
78 | delete this; | |
79 | } | |
11fdf7f2 TL |
80 | virtual bool sync_complete(int r) { |
81 | if (sync_finish(r)) { | |
82 | delete this; | |
83 | return true; | |
84 | } | |
85 | return false; | |
86 | } | |
7c673cae FG |
87 | }; |
88 | ||
89 | /** | |
90 | * Simple context holding a single object | |
91 | */ | |
92 | template<class T> | |
93 | class ContainerContext : public Context { | |
94 | T obj; | |
95 | public: | |
96 | ContainerContext(T &obj) : obj(obj) {} | |
97 | void finish(int r) override {} | |
98 | }; | |
99 | template <typename T> | |
100 | ContainerContext<T> *make_container_context(T &&t) { | |
101 | return new ContainerContext<T>(std::forward<T>(t)); | |
102 | } | |
103 | ||
104 | template <class T> | |
105 | struct Wrapper : public Context { | |
106 | Context *to_run; | |
107 | T val; | |
108 | Wrapper(Context *to_run, T val) : to_run(to_run), val(val) {} | |
109 | void finish(int r) override { | |
110 | if (to_run) | |
111 | to_run->complete(r); | |
112 | } | |
113 | }; | |
114 | struct RunOnDelete { | |
115 | Context *to_run; | |
116 | RunOnDelete(Context *to_run) : to_run(to_run) {} | |
117 | ~RunOnDelete() { | |
118 | if (to_run) | |
119 | to_run->complete(0); | |
120 | } | |
121 | }; | |
11fdf7f2 | 122 | typedef std::shared_ptr<RunOnDelete> RunOnDeleteRef; |
7c673cae FG |
123 | |
124 | template <typename T> | |
125 | struct LambdaContext : public Context { | |
126 | T t; | |
127 | LambdaContext(T &&t) : t(std::forward<T>(t)) {} | |
128 | void finish(int) override { | |
129 | t(); | |
130 | } | |
131 | }; | |
132 | template <typename T> | |
133 | LambdaContext<T> *make_lambda_context(T &&t) { | |
134 | return new LambdaContext<T>(std::move(t)); | |
135 | } | |
136 | ||
137 | template <typename F, typename T> | |
138 | struct LambdaGenContext : GenContext<T> { | |
139 | F f; | |
140 | LambdaGenContext(F &&f) : f(std::forward<F>(f)) {} | |
141 | void finish(T t) override { | |
142 | f(std::forward<T>(t)); | |
143 | } | |
144 | }; | |
145 | template <typename T, typename F> | |
146 | GenContextURef<T> make_gen_lambda_context(F &&f) { | |
147 | return GenContextURef<T>(new LambdaGenContext<F, T>(std::move(f))); | |
148 | } | |
149 | ||
150 | /* | |
151 | * finish and destroy a list of Contexts | |
152 | */ | |
11fdf7f2 TL |
153 | template<class C> |
154 | inline void finish_contexts(CephContext *cct, C& finished, int result = 0) | |
7c673cae FG |
155 | { |
156 | if (finished.empty()) | |
157 | return; | |
158 | ||
11fdf7f2 | 159 | C ls; |
7c673cae FG |
160 | ls.swap(finished); // swap out of place to avoid weird loops |
161 | ||
162 | if (cct) | |
163 | mydout(cct,10) << ls.size() << " contexts to finish with " << result << dendl; | |
11fdf7f2 | 164 | for (Context* c : ls) { |
7c673cae FG |
165 | if (cct) |
166 | mydout(cct,10) << "---- " << c << dendl; | |
167 | c->complete(result); | |
168 | } | |
169 | } | |
170 | ||
171 | class C_NoopContext : public Context { | |
172 | public: | |
173 | void finish(int r) override { } | |
174 | }; | |
175 | ||
176 | ||
177 | struct C_Lock : public Context { | |
178 | Mutex *lock; | |
179 | Context *fin; | |
180 | C_Lock(Mutex *l, Context *c) : lock(l), fin(c) {} | |
181 | ~C_Lock() override { | |
182 | delete fin; | |
183 | } | |
184 | void finish(int r) override { | |
185 | if (fin) { | |
186 | lock->Lock(); | |
187 | fin->complete(r); | |
188 | fin = NULL; | |
189 | lock->Unlock(); | |
190 | } | |
191 | } | |
192 | }; | |
193 | ||
194 | /* | |
195 | * C_Contexts - set of Contexts | |
196 | * | |
197 | * ContextType must be an ancestor class of ContextInstanceType, or the same class. | |
198 | * ContextInstanceType must be default-constructable. | |
199 | */ | |
11fdf7f2 | 200 | template <class ContextType, class ContextInstanceType, class Container = std::list<ContextType *>> |
7c673cae FG |
201 | class C_ContextsBase : public ContextInstanceType { |
202 | public: | |
203 | CephContext *cct; | |
11fdf7f2 | 204 | Container contexts; |
7c673cae FG |
205 | |
206 | C_ContextsBase(CephContext *cct_) | |
207 | : cct(cct_) | |
208 | { | |
209 | } | |
224ce89b WB |
210 | ~C_ContextsBase() override { |
211 | for (auto c : contexts) { | |
212 | delete c; | |
213 | } | |
214 | } | |
7c673cae FG |
215 | void add(ContextType* c) { |
216 | contexts.push_back(c); | |
217 | } | |
11fdf7f2 TL |
218 | void take(Container& ls) { |
219 | Container c; | |
220 | c.swap(ls); | |
221 | if constexpr (std::is_same_v<Container, std::list<ContextType *>>) { | |
222 | contexts.splice(contexts.end(), c); | |
223 | } else { | |
224 | contexts.insert(contexts.end(), c.begin(), c.end()); | |
225 | } | |
7c673cae FG |
226 | } |
227 | void complete(int r) override { | |
228 | // Neuter any ContextInstanceType custom complete(), because although | |
229 | // I want to look like it, I don't actually want to run its code. | |
230 | Context::complete(r); | |
231 | } | |
232 | void finish(int r) override { | |
233 | finish_contexts(cct, contexts, r); | |
234 | } | |
235 | bool empty() { return contexts.empty(); } | |
236 | ||
11fdf7f2 TL |
237 | template<class C> |
238 | static ContextType *list_to_context(C& cs) { | |
7c673cae FG |
239 | if (cs.size() == 0) { |
240 | return 0; | |
241 | } else if (cs.size() == 1) { | |
242 | ContextType *c = cs.front(); | |
243 | cs.clear(); | |
244 | return c; | |
245 | } else { | |
246 | C_ContextsBase<ContextType, ContextInstanceType> *c(new C_ContextsBase<ContextType, ContextInstanceType>(0)); | |
247 | c->take(cs); | |
248 | return c; | |
249 | } | |
250 | } | |
251 | }; | |
252 | ||
253 | typedef C_ContextsBase<Context, Context> C_Contexts; | |
254 | ||
255 | /* | |
256 | * C_Gather | |
257 | * | |
258 | * ContextType must be an ancestor class of ContextInstanceType, or the same class. | |
259 | * ContextInstanceType must be default-constructable. | |
260 | * | |
261 | * BUG:? only reports error from last sub to have an error return | |
262 | */ | |
263 | template <class ContextType, class ContextInstanceType> | |
11fdf7f2 | 264 | class C_GatherBase { |
7c673cae FG |
265 | private: |
266 | CephContext *cct; | |
267 | int result; | |
268 | ContextType *onfinish; | |
269 | #ifdef DEBUG_GATHER | |
270 | std::set<ContextType*> waitfor; | |
271 | #endif | |
272 | int sub_created_count; | |
273 | int sub_existing_count; | |
274 | mutable Mutex lock; | |
275 | bool activated; | |
276 | ||
277 | void sub_finish(ContextType* sub, int r) { | |
278 | lock.Lock(); | |
279 | #ifdef DEBUG_GATHER | |
11fdf7f2 | 280 | ceph_assert(waitfor.count(sub)); |
7c673cae FG |
281 | waitfor.erase(sub); |
282 | #endif | |
283 | --sub_existing_count; | |
284 | mydout(cct,10) << "C_GatherBase " << this << ".sub_finish(r=" << r << ") " << sub | |
285 | #ifdef DEBUG_GATHER | |
286 | << " (remaining " << waitfor << ")" | |
287 | #endif | |
288 | << dendl; | |
289 | if (r < 0 && result == 0) | |
290 | result = r; | |
291 | if ((activated == false) || (sub_existing_count != 0)) { | |
292 | lock.Unlock(); | |
293 | return; | |
294 | } | |
295 | lock.Unlock(); | |
296 | delete_me(); | |
297 | } | |
298 | ||
299 | void delete_me() { | |
300 | if (onfinish) { | |
301 | onfinish->complete(result); | |
302 | onfinish = 0; | |
303 | } | |
304 | delete this; | |
305 | } | |
306 | ||
307 | class C_GatherSub : public ContextInstanceType { | |
308 | C_GatherBase *gather; | |
309 | public: | |
310 | C_GatherSub(C_GatherBase *g) : gather(g) {} | |
311 | void complete(int r) override { | |
312 | // Cancel any customized complete() functionality | |
313 | // from the Context subclass we're templated for, | |
314 | // we only want to hit that in onfinish, not at each | |
315 | // sub finish. e.g. MDSInternalContext. | |
316 | Context::complete(r); | |
317 | } | |
318 | void finish(int r) override { | |
319 | gather->sub_finish(this, r); | |
320 | gather = 0; | |
321 | } | |
322 | ~C_GatherSub() override { | |
323 | if (gather) | |
324 | gather->sub_finish(this, 0); | |
325 | } | |
326 | }; | |
327 | ||
328 | public: | |
329 | C_GatherBase(CephContext *cct_, ContextType *onfinish_) | |
330 | : cct(cct_), result(0), onfinish(onfinish_), | |
331 | sub_created_count(0), sub_existing_count(0), | |
332 | lock("C_GatherBase::lock", true, false), //disable lockdep | |
333 | activated(false) | |
334 | { | |
335 | mydout(cct,10) << "C_GatherBase " << this << ".new" << dendl; | |
336 | } | |
11fdf7f2 | 337 | ~C_GatherBase() { |
7c673cae FG |
338 | mydout(cct,10) << "C_GatherBase " << this << ".delete" << dendl; |
339 | } | |
340 | void set_finisher(ContextType *onfinish_) { | |
341 | Mutex::Locker l(lock); | |
11fdf7f2 | 342 | ceph_assert(!onfinish); |
7c673cae FG |
343 | onfinish = onfinish_; |
344 | } | |
345 | void activate() { | |
346 | lock.Lock(); | |
11fdf7f2 | 347 | ceph_assert(activated == false); |
7c673cae FG |
348 | activated = true; |
349 | if (sub_existing_count != 0) { | |
350 | lock.Unlock(); | |
351 | return; | |
352 | } | |
353 | lock.Unlock(); | |
354 | delete_me(); | |
355 | } | |
356 | ContextType *new_sub() { | |
357 | Mutex::Locker l(lock); | |
11fdf7f2 | 358 | ceph_assert(activated == false); |
7c673cae FG |
359 | sub_created_count++; |
360 | sub_existing_count++; | |
361 | ContextType *s = new C_GatherSub(this); | |
362 | #ifdef DEBUG_GATHER | |
363 | waitfor.insert(s); | |
364 | #endif | |
365 | mydout(cct,10) << "C_GatherBase " << this << ".new_sub is " << sub_created_count << " " << s << dendl; | |
366 | return s; | |
367 | } | |
7c673cae FG |
368 | |
369 | inline int get_sub_existing_count() const { | |
370 | Mutex::Locker l(lock); | |
371 | return sub_existing_count; | |
372 | } | |
373 | ||
374 | inline int get_sub_created_count() const { | |
375 | Mutex::Locker l(lock); | |
376 | return sub_created_count; | |
377 | } | |
378 | }; | |
379 | ||
380 | /* | |
381 | * The C_GatherBuilder remembers each C_Context created by | |
382 | * C_GatherBuilder.new_sub() in a C_Gather. When a C_Context created | |
383 | * by new_sub() is complete(), C_Gather forgets about it. When | |
384 | * C_GatherBuilder notices that there are no C_Context left in | |
385 | * C_Gather, it calls complete() on the C_Context provided as the | |
386 | * second argument of the constructor (finisher). | |
387 | * | |
388 | * How to use C_GatherBuilder: | |
389 | * | |
390 | * 1. Create a C_GatherBuilder on the stack | |
391 | * 2. Call gather_bld.new_sub() as many times as you want to create new subs | |
392 | * It is safe to call this 0 times, or 100, or anything in between. | |
393 | * 3. If you didn't supply a finisher in the C_GatherBuilder constructor, | |
394 | * set one with gather_bld.set_finisher(my_finisher) | |
395 | * 4. Call gather_bld.activate() | |
396 | * | |
397 | * Example: | |
398 | * | |
399 | * C_SaferCond all_done; | |
400 | * C_GatherBuilder gb(g_ceph_context, all_done); | |
401 | * j.submit_entry(1, first, 0, gb.new_sub()); // add a C_Context to C_Gather | |
402 | * j.submit_entry(2, first, 0, gb.new_sub()); // add a C_Context to C_Gather | |
403 | * gb.activate(); // consume C_Context as soon as they complete() | |
404 | * all_done.wait(); // all_done is complete() after all new_sub() are complete() | |
405 | * | |
406 | * The finisher may be called at any point after step 4, including immediately | |
407 | * from the activate() function. | |
408 | * The finisher will never be called before activate(). | |
409 | * | |
410 | * Note: Currently, subs must be manually freed by the caller (for some reason.) | |
411 | */ | |
412 | template <class ContextType, class GatherType> | |
413 | class C_GatherBuilderBase | |
414 | { | |
415 | public: | |
416 | C_GatherBuilderBase(CephContext *cct_) | |
417 | : cct(cct_), c_gather(NULL), finisher(NULL), activated(false) | |
418 | { | |
419 | } | |
420 | C_GatherBuilderBase(CephContext *cct_, ContextType *finisher_) | |
421 | : cct(cct_), c_gather(NULL), finisher(finisher_), activated(false) | |
422 | { | |
423 | } | |
424 | ~C_GatherBuilderBase() { | |
425 | if (c_gather) { | |
11fdf7f2 | 426 | ceph_assert(activated); // Don't forget to activate your C_Gather! |
7c673cae FG |
427 | } |
428 | else { | |
429 | delete finisher; | |
430 | } | |
431 | } | |
432 | ContextType *new_sub() { | |
433 | if (!c_gather) { | |
434 | c_gather = new GatherType(cct, finisher); | |
435 | } | |
436 | return c_gather->new_sub(); | |
437 | } | |
438 | void activate() { | |
439 | if (!c_gather) | |
440 | return; | |
11fdf7f2 | 441 | ceph_assert(finisher != NULL); |
7c673cae FG |
442 | activated = true; |
443 | c_gather->activate(); | |
444 | } | |
445 | void set_finisher(ContextType *finisher_) { | |
446 | finisher = finisher_; | |
447 | if (c_gather) | |
448 | c_gather->set_finisher(finisher); | |
449 | } | |
450 | GatherType *get() const { | |
451 | return c_gather; | |
452 | } | |
453 | bool has_subs() const { | |
454 | return (c_gather != NULL); | |
455 | } | |
456 | int num_subs_created() { | |
11fdf7f2 | 457 | ceph_assert(!activated); |
7c673cae FG |
458 | if (c_gather == NULL) |
459 | return 0; | |
460 | return c_gather->get_sub_created_count(); | |
461 | } | |
462 | int num_subs_remaining() { | |
11fdf7f2 | 463 | ceph_assert(!activated); |
7c673cae FG |
464 | if (c_gather == NULL) |
465 | return 0; | |
466 | return c_gather->get_sub_existing_count(); | |
467 | } | |
468 | ||
469 | private: | |
470 | CephContext *cct; | |
471 | GatherType *c_gather; | |
472 | ContextType *finisher; | |
473 | bool activated; | |
474 | }; | |
475 | ||
476 | typedef C_GatherBase<Context, Context> C_Gather; | |
477 | typedef C_GatherBuilderBase<Context, C_Gather > C_GatherBuilder; | |
478 | ||
479 | class FunctionContext : public Context { | |
480 | public: | |
481 | FunctionContext(boost::function<void(int)> &&callback) | |
482 | : m_callback(std::move(callback)) | |
483 | { | |
484 | } | |
485 | ||
486 | void finish(int r) override { | |
487 | m_callback(r); | |
488 | } | |
489 | private: | |
490 | boost::function<void(int)> m_callback; | |
491 | }; | |
492 | ||
11fdf7f2 TL |
493 | template <class ContextType> |
494 | class ContextFactory { | |
495 | public: | |
496 | virtual ~ContextFactory() {} | |
497 | virtual ContextType *build() = 0; | |
498 | }; | |
499 | ||
7c673cae FG |
500 | #undef mydout |
501 | ||
502 | #endif |