-
Notifications
You must be signed in to change notification settings - Fork 1
/
task_dispatching_framework.h
135 lines (119 loc) · 5.53 KB
/
task_dispatching_framework.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIBHWSEC_TASK_DISPATCHING_FRAMEWORK_H_
#define LIBHWSEC_TASK_DISPATCHING_FRAMEWORK_H_
#include <memory>
#include <string>
#include <utility>
#include <base/bind.h>
#include <base/location.h>
#include <base/threading/thread_task_runner_handle.h>
#include <brillo/dbus/dbus_method_response.h>
namespace hwsec {
// This class allows DBusMethodResponse to Return(), ReplyWithError() or
// destruct from any thread. However, it should be noted that the creation of
// this class should be on the original dbus thread, and this class does not
// handle the situation whereby Return() or ReplyWithError() is called from two
// different threads. (It is the task of the caller to ensure that each instance
// returns only once.)
template <typename... Types>
class ThreadSafeDBusMethodResponse
: public brillo::dbus_utils::DBusMethodResponse<Types...> {
public:
using BaseClass = brillo::dbus_utils::DBusMethodResponse<Types...>;
using DBusMethodResponse = brillo::dbus_utils::DBusMethodResponse<Types...>;
ThreadSafeDBusMethodResponse(ThreadSafeDBusMethodResponse&& callback) =
default;
explicit ThreadSafeDBusMethodResponse(BaseClass&& original_callback)
: BaseClass::DBusMethodResponse(
nullptr,
base::Bind([](std::unique_ptr<dbus::Response>) { NOTREACHED(); })),
origin_task_runner_(base::ThreadTaskRunnerHandle::Get()),
origin_thread_id_(base::PlatformThread::CurrentId()),
original_callback_(new BaseClass(std::move(original_callback))) {}
~ThreadSafeDBusMethodResponse() override {
// The base class can only be destroyed on the original thread,
// because if this method haven't been sent, then it'll try to send an
// empty response, and that may only happen on the original thread.
//
// If we are not on the original thread, we move out the
// |original_callback_|. The callback will be destruct at original thread,
// and this class is safe to destruct in current thread.
if (!IsOnOriginalThread()) {
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce([](const std::unique_ptr<BaseClass>& callback) {},
std::move(original_callback_)));
}
}
void Return(const Types&... return_values) override {
if (IsOnOriginalThread()) {
original_callback_->Return(return_values...);
} else {
// We are not on the original thread, so we'll post it back
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&BaseClass::Return, std::move(original_callback_),
return_values...));
}
}
void ReplyWithError(const brillo::Error* error) override {
if (IsOnOriginalThread()) {
original_callback_->ReplyWithError(error);
} else {
// We are not on the original thread, so we'll post it back.
origin_task_runner_->PostTask(
FROM_HERE, base::BindOnce(
[](std::unique_ptr<BaseClass> callback,
std::unique_ptr<brillo::Error> error) {
callback->ReplyWithError(error.get());
},
std::move(original_callback_), error->Clone()));
}
}
void ReplyWithError(const base::Location& location,
const std::string& error_domain,
const std::string& error_code,
const std::string& error_message) override {
if (IsOnOriginalThread()) {
original_callback_->ReplyWithError(location, error_domain, error_code,
error_message);
} else {
// We are not on the original thread, so we'll post it back.
origin_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<BaseClass> original_callback,
const base::Location& location,
const std::string& error_domain, const std::string& error_code,
const std::string& error_message) {
original_callback->ReplyWithError(location, error_domain,
error_code, error_message);
},
std::move(original_callback_), location, error_domain, error_code,
error_message));
}
}
static std::unique_ptr<DBusMethodResponse> MakeThreadSafe(
std::unique_ptr<DBusMethodResponse> response) {
return std::make_unique<ThreadSafeDBusMethodResponse>(std::move(*response));
}
private:
bool IsOnOriginalThread() const {
return base::PlatformThread::CurrentId() == origin_thread_id_;
}
// We record the task runner and thread id from which this object is created
// so that when Reply(), ReplyWithError() is called, we can verify if it's on
// the original thread, if it's not, we can post it.
scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_;
base::PlatformThreadId origin_thread_id_;
// The instatnce of base class. It is initialized at constructor.
// Because it should operate on the original thread, we will pass it to the
// original thread when needed, and it will deconstruct at the original thread
// when the task is compelete. By the design of original callback, this class
// is not designed to be called twice, and the caller should handle this.
std::unique_ptr<BaseClass> original_callback_;
};
} // namespace hwsec
#endif // LIBHWSEC_TASK_DISPATCHING_FRAMEWORK_H_