Skip to content

Commit 9dbe733

Browse files
authored
Merge pull request emscripten-core#4 from rstz/add-multithreading
Make PThreadFS threadsafe
2 parents 4bf0b60 + 9b7cf77 commit 9dbe733

File tree

5 files changed

+71
-4
lines changed

5 files changed

+71
-4
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// basic file operations
2+
#include <iostream>
3+
#include <fstream>
4+
#include <string>
5+
#include <vector>
6+
#include <thread>
7+
#include <emscripten.h>
8+
#include "pthreadfs.h"
9+
10+
11+
void threadMain(int msg) {
12+
size_t thread_id = std::hash<std::thread::id>{}(std::this_thread::get_id());
13+
std::ofstream myfile;
14+
myfile.open ("filesystemaccess/multi_threading_example", std::ios_base::app);
15+
myfile << "Writing from thread " << msg << " Id: " << thread_id << " ";
16+
myfile.close();
17+
EM_ASM({console.log(`Wrote on thread ${$0}`);}, thread_id);
18+
return;
19+
}
20+
21+
int main () {
22+
emscripten_init_pthreadfs();
23+
EM_ASM({console.log("Hello from main")});
24+
std::remove("filesystemaccess/multi_threading_example");
25+
26+
constexpr int number_of_threads = 10;
27+
28+
std::cout << "Proof that stdout works fine.\n";
29+
30+
std::vector<std::thread> threads;
31+
32+
for (int i = 0; i< number_of_threads; i++) {
33+
std::thread thread(threadMain, i);
34+
threads.push_back(std::move(thread));
35+
}
36+
37+
std::ofstream myfile;
38+
myfile.open ("filesystemaccess/multi_threading_example");
39+
myfile << "Writing the main thread.\n";
40+
myfile.close();
41+
42+
for (int i = 0; i< number_of_threads; i++) {
43+
threads[i].join();
44+
}
45+
46+
EM_ASM({
47+
console.log('Remember to check that the contents of file multi_threading_example are correct.');
48+
});
49+
50+
return 0;
51+
}

pthreadfs/pthreadfs.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ void SyncToAsync::shutdown() {
3333
}
3434

3535
void SyncToAsync::doWork(std::function<void(SyncToAsync::Callback)> newWork) {
36+
// Use the doWorkMutex to prevent more than one doWork being in flight at a
37+
// time, so that this is usable from multiple threads safely.
38+
std::lock_guard<std::mutex> doWorkLock(doWorkMutex);
3639
// Send the work over.
3740
{
3841
std::lock_guard<std::mutex> lock(mutex);
@@ -302,4 +305,4 @@ void emscripten_init_pthreadfs() {
302305
init_fsafs(&resumeWrapper_v);
303306
});
304307
return;
305-
}
308+
}

pthreadfs/pthreadfs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,16 @@ class SyncToAsync {
190190
// Run some work on thread. This is a synchronous call, but the thread can do
191191
// async work for us. To allow us to know when the async work finishes, the
192192
// worker is given a function to call at that time.
193+
//
194+
// It is safe to call this method from multiple threads, as it locks itself.
195+
// That is, you can create an instance of this and call it from multiple
196+
// threads freely.
193197
void doWork(std::function<void(Callback)> newWork);
194198

195199
private:
196200
std::thread thread;
197201
std::mutex mutex;
202+
std::mutex doWorkMutex;
198203
std::condition_variable condition;
199204
std::function<void(Callback)> work;
200205
bool readyToWork = false;
@@ -221,4 +226,4 @@ void resumeWrapper_l(long retVal);
221226

222227
void resumeWrapper_wasi(__wasi_errno_t retVal);
223228

224-
#endif // PTHREADFS_H
229+
#endif // PTHREADFS_H

pthreadfs/src/pthreadfs.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ void SyncToAsync::shutdown() {
3333
}
3434

3535
void SyncToAsync::doWork(std::function<void(SyncToAsync::Callback)> newWork) {
36+
// Use the doWorkMutex to prevent more than one doWork being in flight at a
37+
// time, so that this is usable from multiple threads safely.
38+
std::lock_guard<std::mutex> doWorkLock(doWorkMutex);
3639
// Send the work over.
3740
{
3841
std::lock_guard<std::mutex> lock(mutex);
@@ -302,4 +305,4 @@ void emscripten_init_pthreadfs() {
302305
init_fsafs(&resumeWrapper_v);
303306
});
304307
return;
305-
}
308+
}

pthreadfs/src/pthreadfs.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,16 @@ class SyncToAsync {
190190
// Run some work on thread. This is a synchronous call, but the thread can do
191191
// async work for us. To allow us to know when the async work finishes, the
192192
// worker is given a function to call at that time.
193+
//
194+
// It is safe to call this method from multiple threads, as it locks itself.
195+
// That is, you can create an instance of this and call it from multiple
196+
// threads freely.
193197
void doWork(std::function<void(Callback)> newWork);
194198

195199
private:
196200
std::thread thread;
197201
std::mutex mutex;
202+
std::mutex doWorkMutex;
198203
std::condition_variable condition;
199204
std::function<void(Callback)> work;
200205
bool readyToWork = false;
@@ -221,4 +226,4 @@ void resumeWrapper_l(long retVal);
221226

222227
void resumeWrapper_wasi(__wasi_errno_t retVal);
223228

224-
#endif // PTHREADFS_H
229+
#endif // PTHREADFS_H

0 commit comments

Comments
 (0)