@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.
Files changed (45) hide show
  1. package/android/CMakeLists.txt +1 -1
  2. package/android/build.gradle +1 -1
  3. package/android/cpp-adapter.cpp +1 -1
  4. package/android/src/main/java/com/op/sqlite/OPSQLiteModule.kt +7 -9
  5. package/cpp/DBHostObject.cpp +469 -677
  6. package/cpp/DBHostObject.h +56 -58
  7. package/cpp/DumbHostObject.cpp +1 -1
  8. package/cpp/DumbHostObject.h +12 -13
  9. package/cpp/OPSqlite.cpp +207 -0
  10. package/cpp/OPThreadPool.cpp +79 -79
  11. package/cpp/OPThreadPool.h +28 -28
  12. package/cpp/PreparedStatementHostObject.cpp +87 -136
  13. package/cpp/PreparedStatementHostObject.h +16 -28
  14. package/cpp/SmartHostObject.cpp +1 -1
  15. package/cpp/SmartHostObject.h +6 -7
  16. package/cpp/bridge.cpp +639 -633
  17. package/cpp/bridge.h +2 -2
  18. package/cpp/libsql/LICENSE.txt +9 -0
  19. package/cpp/libsql/bridge.cpp +2 -2
  20. package/cpp/libsql/{bridge.h → bridge.hpp} +4 -4
  21. package/cpp/macros.hpp +21 -0
  22. package/cpp/sqlcipher/LICENSE.txt +24 -0
  23. package/cpp/types.hpp +42 -0
  24. package/cpp/utils.cpp +320 -255
  25. package/cpp/{utils.h → utils.hpp} +9 -1
  26. package/ios/OPSQLite.mm +104 -106
  27. package/lib/module/functions.js +52 -44
  28. package/lib/module/functions.js.map +1 -1
  29. package/lib/module/index.js +1 -1
  30. package/lib/module/index.js.map +1 -1
  31. package/lib/typescript/src/functions.d.ts +5 -1
  32. package/lib/typescript/src/functions.d.ts.map +1 -1
  33. package/lib/typescript/src/index.d.ts +1 -1
  34. package/lib/typescript/src/index.d.ts.map +1 -1
  35. package/lib/typescript/src/types.d.ts +12 -1
  36. package/lib/typescript/src/types.d.ts.map +1 -1
  37. package/op-sqlite.podspec +1 -1
  38. package/package.json +10 -8
  39. package/src/functions.ts +64 -54
  40. package/src/index.ts +1 -12
  41. package/src/types.ts +9 -1
  42. package/cpp/bindings.cpp +0 -202
  43. package/cpp/macros.h +0 -15
  44. package/cpp/types.h +0 -33
  45. /package/cpp/{bindings.h → OPSqlite.hpp} +0 -0
@@ -1,12 +1,12 @@
1
1
  #pragma once
2
2
 
3
3
  #include "OPThreadPool.h"
4
- #include "types.h"
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.h"
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
- std::string db_name;
27
- std::string table;
28
- std::string rowid;
26
+ std::string db_name;
27
+ std::string table;
28
+ std::string rowid;
29
29
  };
30
30
 
31
31
  struct TableRowDiscriminator {
32
- std::string table;
33
- std::vector<int> ids;
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
- sqlite3_stmt *stmt;
38
+ sqlite3_stmt *stmt;
39
39
  #endif
40
- std::vector<TableRowDiscriminator> discriminators;
41
- std::shared_ptr<jsi::Value> callback;
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
- 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);
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
- // 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);
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
- // 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,
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
- std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
67
- jsi::Value get(jsi::Runtime &rt,
68
- const jsi::PropNameID &propNameID) override;
69
- void set(jsi::Runtime &rt, const jsi::PropNameID &name,
70
- const jsi::Value &value) override;
71
- void on_update(const std::string &table, const std::string &operation,
72
- long long row_id);
73
- void on_commit();
74
- void on_rollback();
75
- void invalidate();
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
- private:
79
- std::set<std::shared_ptr<ReactiveQuery>> pending_reactive_queries;
80
- void auto_register_update_hook();
81
- void create_jsi_functions();
82
- void
83
- flush_pending_reactive_queries(const std::shared_ptr<jsi::Value> &resolve);
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
- std::unordered_map<std::string, jsi::Value> function_map;
86
- std::string base_path;
87
- std::shared_ptr<react::CallInvoker> invoker;
88
- std::shared_ptr<ThreadPool> _thread_pool;
89
- std::string db_name;
90
- std::shared_ptr<jsi::Value> update_hook_callback;
91
- std::shared_ptr<jsi::Value> commit_hook_callback;
92
- std::shared_ptr<jsi::Value> rollback_hook_callback;
93
- jsi::Runtime &rt;
94
- std::vector<std::shared_ptr<ReactiveQuery>> reactive_queries;
95
- std::vector<PendingReactiveInvocation> pending_reactive_invocations;
96
- bool is_update_hook_registered = false;
97
- bool invalidated = false;
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
- DB db;
97
+ DB db;
100
98
  #else
101
- sqlite3 *db;
99
+ sqlite3 *db;
102
100
  #endif
103
101
  };
104
102
 
@@ -1,6 +1,6 @@
1
1
  #include "DumbHostObject.h"
2
2
  #include "SmartHostObject.h"
3
- #include "utils.h"
3
+ #include "utils.hpp"
4
4
  #include <iostream>
5
5
 
6
6
  namespace opsqlite {
@@ -1,7 +1,7 @@
1
1
  #pragma once
2
2
 
3
3
  #include "SmartHostObject.h"
4
- #include "types.h"
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
- public:
16
- DumbHostObject() = default;
15
+ public:
16
+ DumbHostObject() = default;
17
17
 
18
- explicit DumbHostObject(
19
- std::shared_ptr<std::vector<SmartHostObject>> metadata);
18
+ explicit DumbHostObject(
19
+ std::shared_ptr<std::vector<SmartHostObject>> metadata);
20
20
 
21
- std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
21
+ std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
22
22
 
23
- jsi::Value get(jsi::Runtime &rt,
24
- const jsi::PropNameID &propNameID) override;
23
+ jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &propNameID) override;
25
24
 
26
- void set(jsi::Runtime &rt, const jsi::PropNameID &name,
27
- const jsi::Value &value) override;
25
+ void set(jsi::Runtime &rt, const jsi::PropNameID &name,
26
+ const jsi::Value &value) override;
28
27
 
29
- std::vector<JSVariant> values;
28
+ std::vector<JSVariant> values;
30
29
 
31
- std::shared_ptr<std::vector<SmartHostObject>> metadata;
30
+ std::shared_ptr<std::vector<SmartHostObject>> metadata;
32
31
 
33
- std::vector<std::pair<std::string, JSVariant>> ownValues;
32
+ std::vector<std::pair<std::string, JSVariant>> ownValues;
34
33
  };
35
34
 
36
35
  } // namespace opsqlite
@@ -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
@@ -3,118 +3,118 @@
3
3
  namespace opsqlite {
4
4
 
5
5
  ThreadPool::ThreadPool() : done(false) {
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
- }
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
- // So threads know it's time to shut down
28
- done = true;
27
+ // So threads know it's time to shut down
28
+ done = true;
29
29
 
30
- // Wake up all the threads, so they can finish and be joined
31
- workQueueConditionVariable.notify_all();
30
+ // Wake up all the threads, so they can finish and be joined
31
+ workQueueConditionVariable.notify_all();
32
32
 
33
- for (auto &thread : threads) {
34
- if (thread.joinable()) {
35
- thread.join();
36
- }
33
+ for (auto &thread : threads) {
34
+ if (thread.joinable()) {
35
+ thread.join();
37
36
  }
37
+ }
38
38
 
39
- threads.clear();
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
- // Grab the mutex
46
- std::lock_guard<std::mutex> g(workQueueMutex);
45
+ // Grab the mutex
46
+ std::lock_guard<std::mutex> g(workQueueMutex);
47
47
 
48
- // Push the request to the queue
49
- workQueue.push(task);
48
+ // Push the request to the queue
49
+ workQueue.push(task);
50
50
 
51
- // Notify one thread that there are requests to process
52
- workQueueConditionVariable.notify_one();
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
- // 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();
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
- std::unique_lock<std::mutex> g(workQueueMutex);
86
- workQueueConditionVariable.wait(
87
- g, [&] { return workQueue.empty() && (busy == 0); });
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
- // So threads know it's time to shut down
92
- done = true;
91
+ // So threads know it's time to shut down
92
+ done = true;
93
93
 
94
- // Wake up all the threads, so they can finish and be joined
95
- workQueueConditionVariable.notify_all();
94
+ // Wake up all the threads, so they can finish and be joined
95
+ workQueueConditionVariable.notify_all();
96
96
 
97
- for (auto &thread : threads) {
98
- if (thread.joinable()) {
99
- thread.join();
100
- }
97
+ for (auto &thread : threads) {
98
+ if (thread.joinable()) {
99
+ thread.join();
101
100
  }
101
+ }
102
102
 
103
- threads.clear();
103
+ threads.clear();
104
104
 
105
- auto numberOfThreads = std::thread::hardware_concurrency();
106
- if (numberOfThreads == 0) {
107
- numberOfThreads = 1;
108
- }
105
+ auto numberOfThreads = std::thread::hardware_concurrency();
106
+ if (numberOfThreads == 0) {
107
+ numberOfThreads = 1;
108
+ }
109
109
 
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
- }
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
- done = false;
118
+ done = false;
119
119
  }
120
120
  } // namespace opsqlite
@@ -11,34 +11,34 @@
11
11
  namespace opsqlite {
12
12
 
13
13
  class ThreadPool {
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();
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