@op-engineering/op-sqlite 15.0.7 → 15.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/CMakeLists.txt +1 -1
- package/android/build.gradle +1 -1
- package/android/cpp-adapter.cpp +1 -1
- package/android/src/main/java/com/op/sqlite/OPSQLiteModule.kt +7 -9
- package/cpp/DBHostObject.cpp +469 -677
- package/cpp/DBHostObject.h +56 -58
- package/cpp/DumbHostObject.cpp +1 -1
- package/cpp/DumbHostObject.h +12 -13
- package/cpp/OPSqlite.cpp +207 -0
- package/cpp/OPThreadPool.cpp +79 -79
- package/cpp/OPThreadPool.h +28 -28
- package/cpp/PreparedStatementHostObject.cpp +87 -136
- package/cpp/PreparedStatementHostObject.h +16 -28
- package/cpp/SmartHostObject.cpp +1 -1
- package/cpp/SmartHostObject.h +6 -7
- package/cpp/bridge.cpp +639 -633
- package/cpp/bridge.h +2 -2
- package/cpp/libsql/LICENSE.txt +9 -0
- package/cpp/libsql/bridge.cpp +2 -2
- package/cpp/libsql/{bridge.h → bridge.hpp} +4 -4
- package/cpp/macros.hpp +21 -0
- package/cpp/sqlcipher/LICENSE.txt +24 -0
- package/cpp/types.hpp +42 -0
- package/cpp/utils.cpp +320 -255
- package/cpp/{utils.h → utils.hpp} +9 -1
- package/ios/OPSQLite.mm +104 -106
- package/lib/module/functions.js +52 -44
- package/lib/module/functions.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/functions.d.ts +5 -1
- package/lib/typescript/src/functions.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +12 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/op-sqlite.podspec +1 -1
- package/package.json +10 -8
- package/src/functions.ts +64 -54
- package/src/index.ts +1 -12
- package/src/types.ts +9 -1
- package/cpp/bindings.cpp +0 -202
- package/cpp/macros.h +0 -15
- package/cpp/types.h +0 -33
- /package/cpp/{bindings.h → OPSqlite.hpp} +0 -0
package/cpp/DBHostObject.h
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include "OPThreadPool.h"
|
|
4
|
-
#include "types.
|
|
4
|
+
#include "types.hpp"
|
|
5
5
|
#include <ReactCommon/CallInvoker.h>
|
|
6
6
|
#include <jsi/jsi.h>
|
|
7
7
|
#include <set>
|
|
8
8
|
#ifdef OP_SQLITE_USE_LIBSQL
|
|
9
|
-
#include "libsql/bridge.
|
|
9
|
+
#include "libsql/bridge.hpp"
|
|
10
10
|
#else
|
|
11
11
|
#ifdef __ANDROID__
|
|
12
12
|
#include "sqlite3.h"
|
|
@@ -23,82 +23,80 @@ namespace jsi = facebook::jsi;
|
|
|
23
23
|
namespace react = facebook::react;
|
|
24
24
|
|
|
25
25
|
struct PendingReactiveInvocation {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
std::string db_name;
|
|
27
|
+
std::string table;
|
|
28
|
+
std::string rowid;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
struct TableRowDiscriminator {
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
std::string table;
|
|
33
|
+
std::vector<int> ids;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
36
|
struct ReactiveQuery {
|
|
37
37
|
#ifndef OP_SQLITE_USE_LIBSQL
|
|
38
|
-
|
|
38
|
+
sqlite3_stmt *stmt;
|
|
39
39
|
#endif
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
std::vector<TableRowDiscriminator> discriminators;
|
|
41
|
+
std::shared_ptr<jsi::Value> callback;
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
class JSI_EXPORT DBHostObject : public jsi::HostObject {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
public:
|
|
46
|
+
// Normal constructor shared between all backends
|
|
47
|
+
DBHostObject(jsi::Runtime &rt, std::string &base_path,
|
|
48
|
+
std::shared_ptr<react::CallInvoker> invoker,
|
|
49
|
+
std::string &db_name, std::string &path,
|
|
50
|
+
std::string &crsqlite_path, std::string &sqlite_vec_path,
|
|
51
|
+
std::string &encryption_key);
|
|
52
52
|
|
|
53
53
|
#ifdef OP_SQLITE_USE_LIBSQL
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
// Constructor for remoteOpen, purely for remote databases
|
|
55
|
+
DBHostObject(jsi::Runtime &rt, std::string &url, std::string &auth_token,
|
|
56
|
+
std::shared_ptr<react::CallInvoker> invoker);
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
std::string &remote_encryption_key);
|
|
58
|
+
// Constructor for a local database with remote sync
|
|
59
|
+
DBHostObject(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> invoker,
|
|
60
|
+
std::string &db_name, std::string &path, std::string &url,
|
|
61
|
+
std::string &auth_token, int sync_interval, bool offline,
|
|
62
|
+
std::string &encryption_key, std::string &remote_encryption_key);
|
|
64
63
|
#endif
|
|
65
64
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
~DBHostObject() override;
|
|
65
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
|
|
66
|
+
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propNameID) override;
|
|
67
|
+
void set(jsi::Runtime &rt, const jsi::PropNameID &name,
|
|
68
|
+
const jsi::Value &value) override;
|
|
69
|
+
void on_update(const std::string &table, const std::string &operation,
|
|
70
|
+
long long row_id);
|
|
71
|
+
void on_commit();
|
|
72
|
+
void on_rollback();
|
|
73
|
+
void invalidate();
|
|
74
|
+
~DBHostObject() override;
|
|
77
75
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
76
|
+
private:
|
|
77
|
+
std::set<std::shared_ptr<ReactiveQuery>> pending_reactive_queries;
|
|
78
|
+
void auto_register_update_hook();
|
|
79
|
+
void create_jsi_functions();
|
|
80
|
+
void
|
|
81
|
+
flush_pending_reactive_queries(const std::shared_ptr<jsi::Value> &resolve);
|
|
84
82
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
83
|
+
std::unordered_map<std::string, jsi::Value> function_map;
|
|
84
|
+
std::string base_path;
|
|
85
|
+
std::shared_ptr<react::CallInvoker> invoker;
|
|
86
|
+
std::shared_ptr<ThreadPool> _thread_pool;
|
|
87
|
+
std::string db_name;
|
|
88
|
+
std::shared_ptr<jsi::Value> update_hook_callback;
|
|
89
|
+
std::shared_ptr<jsi::Value> commit_hook_callback;
|
|
90
|
+
std::shared_ptr<jsi::Value> rollback_hook_callback;
|
|
91
|
+
jsi::Runtime &rt;
|
|
92
|
+
std::vector<std::shared_ptr<ReactiveQuery>> reactive_queries;
|
|
93
|
+
std::vector<PendingReactiveInvocation> pending_reactive_invocations;
|
|
94
|
+
bool is_update_hook_registered = false;
|
|
95
|
+
bool invalidated = false;
|
|
98
96
|
#ifdef OP_SQLITE_USE_LIBSQL
|
|
99
|
-
|
|
97
|
+
DB db;
|
|
100
98
|
#else
|
|
101
|
-
|
|
99
|
+
sqlite3 *db;
|
|
102
100
|
#endif
|
|
103
101
|
};
|
|
104
102
|
|
package/cpp/DumbHostObject.cpp
CHANGED
package/cpp/DumbHostObject.h
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include "SmartHostObject.h"
|
|
4
|
-
#include "types.
|
|
4
|
+
#include "types.hpp"
|
|
5
5
|
#include <any>
|
|
6
6
|
#include <jsi/jsi.h>
|
|
7
7
|
#include <stdio.h>
|
|
@@ -12,25 +12,24 @@ namespace opsqlite {
|
|
|
12
12
|
namespace jsi = facebook::jsi;
|
|
13
13
|
|
|
14
14
|
class JSI_EXPORT DumbHostObject : public jsi::HostObject {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
public:
|
|
16
|
+
DumbHostObject() = default;
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
explicit DumbHostObject(
|
|
19
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadata);
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
const jsi::PropNameID &propNameID) override;
|
|
23
|
+
jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propNameID) override;
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
void set(jsi::Runtime &rt, const jsi::PropNameID &name,
|
|
26
|
+
const jsi::Value &value) override;
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
std::vector<JSVariant> values;
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
std::shared_ptr<std::vector<SmartHostObject>> metadata;
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
std::vector<std::pair<std::string, JSVariant>> ownValues;
|
|
34
33
|
};
|
|
35
34
|
|
|
36
35
|
} // namespace opsqlite
|
package/cpp/OPSqlite.cpp
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
#include "OPSqlite.hpp"
|
|
2
|
+
#include "DBHostObject.h"
|
|
3
|
+
#include "DumbHostObject.h"
|
|
4
|
+
#include "OPThreadPool.h"
|
|
5
|
+
#ifdef OP_SQLITE_USE_LIBSQL
|
|
6
|
+
#include "libsql/bridge.hpp"
|
|
7
|
+
#else
|
|
8
|
+
#include "bridge.h"
|
|
9
|
+
#endif
|
|
10
|
+
#include "logs.h"
|
|
11
|
+
#include "macros.hpp"
|
|
12
|
+
#include "utils.hpp"
|
|
13
|
+
#include <iostream>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <unordered_map>
|
|
16
|
+
#include <vector>
|
|
17
|
+
|
|
18
|
+
namespace opsqlite {
|
|
19
|
+
|
|
20
|
+
namespace jsi = facebook::jsi;
|
|
21
|
+
namespace react = facebook::react;
|
|
22
|
+
|
|
23
|
+
std::string _base_path;
|
|
24
|
+
std::string _crsqlite_path;
|
|
25
|
+
std::string _sqlite_vec_path;
|
|
26
|
+
std::vector<std::shared_ptr<DBHostObject>> dbs;
|
|
27
|
+
bool invalidated = false;
|
|
28
|
+
std::shared_ptr<react::CallInvoker> invoker;
|
|
29
|
+
|
|
30
|
+
// React native will try to clean the module on JS context invalidation
|
|
31
|
+
// (CodePush/Hot Reload) The clearState function is called
|
|
32
|
+
void invalidate() {
|
|
33
|
+
// Global flag used by the threads to stop work
|
|
34
|
+
invalidated = true;
|
|
35
|
+
|
|
36
|
+
for (const auto &db : dbs) {
|
|
37
|
+
db->invalidate();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Clear our existing vector of shared pointers so they can be garbage
|
|
41
|
+
// collected
|
|
42
|
+
dbs.clear();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
void install(jsi::Runtime &rt,
|
|
46
|
+
const std::shared_ptr<react::CallInvoker> &invoker,
|
|
47
|
+
const char *base_path, const char *crsqlite_path,
|
|
48
|
+
const char *sqlite_vec_path) {
|
|
49
|
+
|
|
50
|
+
_base_path = std::string(base_path);
|
|
51
|
+
_crsqlite_path = std::string(crsqlite_path);
|
|
52
|
+
_sqlite_vec_path = std::string(sqlite_vec_path);
|
|
53
|
+
opsqlite::invoker = invoker;
|
|
54
|
+
|
|
55
|
+
auto open = HFN0 {
|
|
56
|
+
jsi::Object options = args[0].asObject(rt);
|
|
57
|
+
std::string name = options.getProperty(rt, "name").asString(rt).utf8(rt);
|
|
58
|
+
std::string path = std::string(_base_path);
|
|
59
|
+
std::string location;
|
|
60
|
+
std::string encryption_key;
|
|
61
|
+
|
|
62
|
+
if (options.hasProperty(rt, "location")) {
|
|
63
|
+
location = options.getProperty(rt, "location").asString(rt).utf8(rt);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (options.hasProperty(rt, "encryptionKey")) {
|
|
67
|
+
encryption_key =
|
|
68
|
+
options.getProperty(rt, "encryptionKey").asString(rt).utf8(rt);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
72
|
+
if (encryption_key.empty()) {
|
|
73
|
+
log_to_console(rt, "Encryption key is missing for SQLCipher");
|
|
74
|
+
}
|
|
75
|
+
#endif
|
|
76
|
+
|
|
77
|
+
if (!location.empty()) {
|
|
78
|
+
if (location == ":memory:") {
|
|
79
|
+
path = ":memory:";
|
|
80
|
+
} else if (location.rfind('/', 0) == 0) {
|
|
81
|
+
path = location;
|
|
82
|
+
} else {
|
|
83
|
+
path = path + "/" + location;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
std::shared_ptr<DBHostObject> db = std::make_shared<DBHostObject>(
|
|
88
|
+
rt, path, opsqlite::invoker, name, path, _crsqlite_path,
|
|
89
|
+
_sqlite_vec_path, encryption_key);
|
|
90
|
+
dbs.emplace_back(db);
|
|
91
|
+
return jsi::Object::createFromHostObject(rt, db);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
auto is_sqlcipher = HOST_STATIC_FN("isSQLCipher") {
|
|
95
|
+
#ifdef OP_SQLITE_USE_SQLCIPHER
|
|
96
|
+
return true;
|
|
97
|
+
#else
|
|
98
|
+
return false;
|
|
99
|
+
#endif
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
auto is_ios_embedded = HOST_STATIC_FN("isIOSEmbedded") {
|
|
103
|
+
#ifdef OP_SQLITE_USE_PHONE_VERSION
|
|
104
|
+
return true;
|
|
105
|
+
#else
|
|
106
|
+
return false;
|
|
107
|
+
#endif
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
auto is_libsql = HOST_STATIC_FN("isLibsql") {
|
|
111
|
+
#ifdef OP_SQLITE_USE_LIBSQL
|
|
112
|
+
return true;
|
|
113
|
+
#else
|
|
114
|
+
return false;
|
|
115
|
+
#endif
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
#ifdef OP_SQLITE_USE_LIBSQL
|
|
119
|
+
auto open_remote = HOST_STATIC_FN("openRemote") {
|
|
120
|
+
jsi::Object options = args[0].asObject(rt);
|
|
121
|
+
|
|
122
|
+
std::string url = options.getProperty(rt, "url").asString(rt).utf8(rt);
|
|
123
|
+
|
|
124
|
+
std::string auth_token =
|
|
125
|
+
options.getProperty(rt, "authToken").asString(rt).utf8(rt);
|
|
126
|
+
|
|
127
|
+
std::shared_ptr<DBHostObject> db =
|
|
128
|
+
std::make_shared<DBHostObject>(rt, url, auth_token, invoker);
|
|
129
|
+
|
|
130
|
+
return jsi::Object::createFromHostObject(rt, db);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
auto open_sync = HOST_STATIC_FN("openSync") {
|
|
134
|
+
jsi::Object options = args[0].asObject(rt);
|
|
135
|
+
std::string name = options.getProperty(rt, "name").asString(rt).utf8(rt);
|
|
136
|
+
std::string path = std::string(_base_path);
|
|
137
|
+
std::string url = options.getProperty(rt, "url").asString(rt).utf8(rt);
|
|
138
|
+
std::string auth_token =
|
|
139
|
+
options.getProperty(rt, "authToken").asString(rt).utf8(rt);
|
|
140
|
+
|
|
141
|
+
int sync_interval = 0;
|
|
142
|
+
if (options.hasProperty(rt, "libsqlSyncInterval")) {
|
|
143
|
+
sync_interval = static_cast<int>(
|
|
144
|
+
options.getProperty(rt, "libsqlSyncInterval").asNumber());
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
bool offline = false;
|
|
148
|
+
if (options.hasProperty(rt, "libsqlOffline")) {
|
|
149
|
+
offline = options.getProperty(rt, "libsqlOffline").asBool();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
std::string encryption_key;
|
|
153
|
+
if (options.hasProperty(rt, "encryptionKey")) {
|
|
154
|
+
encryption_key =
|
|
155
|
+
options.getProperty(rt, "encryptionKey").asString(rt).utf8(rt);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
std::string remote_encryption_key;
|
|
159
|
+
if (options.hasProperty(rt, "remoteEncryptionKey")) {
|
|
160
|
+
remote_encryption_key =
|
|
161
|
+
options.getProperty(rt, "remoteEncryptionKey").asString(rt).utf8(rt);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
std::string location;
|
|
165
|
+
if (options.hasProperty(rt, "location")) {
|
|
166
|
+
location = options.getProperty(rt, "location").asString(rt).utf8(rt);
|
|
167
|
+
}
|
|
168
|
+
if (!location.empty()) {
|
|
169
|
+
if (location == ":memory:") {
|
|
170
|
+
path = ":memory:";
|
|
171
|
+
} else if (location.rfind("/", 0) == 0) {
|
|
172
|
+
path = location;
|
|
173
|
+
} else {
|
|
174
|
+
path = path + "/" + location;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
std::shared_ptr<DBHostObject> db = std::make_shared<DBHostObject>(
|
|
179
|
+
rt, invoker, name, path, url, auth_token, sync_interval, offline,
|
|
180
|
+
encryption_key, remote_encryption_key);
|
|
181
|
+
return jsi::Object::createFromHostObject(rt, db);
|
|
182
|
+
});
|
|
183
|
+
#endif
|
|
184
|
+
|
|
185
|
+
jsi::Object module = jsi::Object(rt);
|
|
186
|
+
module.setProperty(rt, "open", std::move(open));
|
|
187
|
+
module.setProperty(rt, "isSQLCipher", std::move(is_sqlcipher));
|
|
188
|
+
module.setProperty(rt, "isLibsql", std::move(is_libsql));
|
|
189
|
+
module.setProperty(rt, "isIOSEmbedded", std::move(is_ios_embedded));
|
|
190
|
+
#ifdef OP_SQLITE_USE_LIBSQL
|
|
191
|
+
module.setProperty(rt, "openRemote", std::move(open_remote));
|
|
192
|
+
module.setProperty(rt, "openSync", std::move(open_sync));
|
|
193
|
+
#endif
|
|
194
|
+
|
|
195
|
+
rt.global().setProperty(rt, "__OPSQLiteProxy", std::move(module));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
void expoUpdatesWorkaround(const char *base_path) {
|
|
199
|
+
#ifdef OP_SQLITE_USE_LIBSQL
|
|
200
|
+
std::string path = std::string(base_path);
|
|
201
|
+
// Open a DB before anything else so that expo-updates does not mess up the
|
|
202
|
+
// configuration
|
|
203
|
+
opsqlite_libsql_open("__dummy", path, "");
|
|
204
|
+
#endif
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
} // namespace opsqlite
|
package/cpp/OPThreadPool.cpp
CHANGED
|
@@ -3,118 +3,118 @@
|
|
|
3
3
|
namespace opsqlite {
|
|
4
4
|
|
|
5
5
|
ThreadPool::ThreadPool() : done(false) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
6
|
+
// This returns the number of threads supported by the system. If the
|
|
7
|
+
// function can't figure out this information, it returns 0. 0 is not good,
|
|
8
|
+
// so we create at least 1
|
|
9
|
+
// auto numberOfThreads = std::thread::hardware_concurrency();
|
|
10
|
+
// if (numberOfThreads == 0) {
|
|
11
|
+
// numberOfThreads = 1;
|
|
12
|
+
// }
|
|
13
|
+
|
|
14
|
+
auto numberOfThreads = 1;
|
|
15
|
+
for (unsigned i = 0; i < numberOfThreads; ++i) {
|
|
16
|
+
// The threads will execute the private member `doWork`. Note that we
|
|
17
|
+
// need to pass a reference to the function (namespaced with the class
|
|
18
|
+
// name) as the first argument, and the current object as second
|
|
19
|
+
// argument
|
|
20
|
+
threads.emplace_back(&ThreadPool::doWork, this);
|
|
21
|
+
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
// The destructor joins all the threads so the program can exit gracefully.
|
|
25
25
|
// This will be executed if there is any exception (e.g. creating the threads)
|
|
26
26
|
ThreadPool::~ThreadPool() {
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
// So threads know it's time to shut down
|
|
28
|
+
done = true;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
// Wake up all the threads, so they can finish and be joined
|
|
31
|
+
workQueueConditionVariable.notify_all();
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
33
|
+
for (auto &thread : threads) {
|
|
34
|
+
if (thread.joinable()) {
|
|
35
|
+
thread.join();
|
|
37
36
|
}
|
|
37
|
+
}
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
threads.clear();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// This function will be called by the server every time there is a request
|
|
43
43
|
// that needs to be processed by the thread pool
|
|
44
44
|
void ThreadPool::queueWork(const std::function<void(void)> &task) {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Grab the mutex
|
|
46
|
+
std::lock_guard<std::mutex> g(workQueueMutex);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
// Push the request to the queue
|
|
49
|
+
workQueue.push(task);
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
// Notify one thread that there are requests to process
|
|
52
|
+
workQueueConditionVariable.notify_one();
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// Function used by the threads to grab work from the queue
|
|
56
56
|
void ThreadPool::doWork() {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
++busy;
|
|
79
|
-
task();
|
|
80
|
-
--busy;
|
|
57
|
+
// Loop while the queue is not destructing
|
|
58
|
+
while (!done) {
|
|
59
|
+
std::function<void(void)> task;
|
|
60
|
+
|
|
61
|
+
// Create a scope, so we don't lock the queue for longer than necessary
|
|
62
|
+
{
|
|
63
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
64
|
+
workQueueConditionVariable.wait(g, [&] {
|
|
65
|
+
// Only wake up if there are elements in the queue or the
|
|
66
|
+
// program is shutting down
|
|
67
|
+
return !workQueue.empty() || done;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// If we are shutting down exit without trying to process more work
|
|
71
|
+
if (done) {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
task = workQueue.front();
|
|
76
|
+
workQueue.pop();
|
|
81
77
|
}
|
|
78
|
+
++busy;
|
|
79
|
+
task();
|
|
80
|
+
--busy;
|
|
81
|
+
}
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
void ThreadPool::waitFinished() {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
std::unique_lock<std::mutex> g(workQueueMutex);
|
|
86
|
+
workQueueConditionVariable.wait(
|
|
87
|
+
g, [&] { return workQueue.empty() && (busy == 0); });
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
void ThreadPool::restartPool() {
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
// So threads know it's time to shut down
|
|
92
|
+
done = true;
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
// Wake up all the threads, so they can finish and be joined
|
|
95
|
+
workQueueConditionVariable.notify_all();
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
97
|
+
for (auto &thread : threads) {
|
|
98
|
+
if (thread.joinable()) {
|
|
99
|
+
thread.join();
|
|
101
100
|
}
|
|
101
|
+
}
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
threads.clear();
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
105
|
+
auto numberOfThreads = std::thread::hardware_concurrency();
|
|
106
|
+
if (numberOfThreads == 0) {
|
|
107
|
+
numberOfThreads = 1;
|
|
108
|
+
}
|
|
109
109
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
110
|
+
for (unsigned i = 0; i < numberOfThreads; ++i) {
|
|
111
|
+
// The threads will execute the private member `doWork`. Note that we
|
|
112
|
+
// need to pass a reference to the function (namespaced with the class
|
|
113
|
+
// name) as the first argument, and the current object as second
|
|
114
|
+
// argument
|
|
115
|
+
threads.emplace_back(&ThreadPool::doWork, this);
|
|
116
|
+
}
|
|
117
117
|
|
|
118
|
-
|
|
118
|
+
done = false;
|
|
119
119
|
}
|
|
120
120
|
} // namespace opsqlite
|
package/cpp/OPThreadPool.h
CHANGED
|
@@ -11,34 +11,34 @@
|
|
|
11
11
|
namespace opsqlite {
|
|
12
12
|
|
|
13
13
|
class ThreadPool {
|
|
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
|
-
|
|
14
|
+
public:
|
|
15
|
+
ThreadPool();
|
|
16
|
+
~ThreadPool();
|
|
17
|
+
void queueWork(const std::function<void(void)> &task);
|
|
18
|
+
void waitFinished();
|
|
19
|
+
void restartPool();
|
|
20
|
+
|
|
21
|
+
private:
|
|
22
|
+
unsigned int busy{};
|
|
23
|
+
// This condition variable is used for the threads to wait until there is
|
|
24
|
+
// work to do
|
|
25
|
+
std::condition_variable_any workQueueConditionVariable;
|
|
26
|
+
|
|
27
|
+
// We store the threads in a vector, so we can later stop them gracefully
|
|
28
|
+
std::vector<std::thread> threads;
|
|
29
|
+
|
|
30
|
+
// Mutex to protect workQueue
|
|
31
|
+
std::mutex workQueueMutex;
|
|
32
|
+
|
|
33
|
+
// Queue of requests waiting to be processed
|
|
34
|
+
std::queue<std::function<void(void)>> workQueue;
|
|
35
|
+
|
|
36
|
+
// This will be set to true when the thread pool is shutting down. This
|
|
37
|
+
// tells the threads to stop looping and finish
|
|
38
|
+
bool done;
|
|
39
|
+
|
|
40
|
+
// Function used by the threads to grab work from the queue
|
|
41
|
+
void doWork();
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
} // namespace opsqlite
|