1414#include < node_api.h>
1515#include < assert.h>
1616#include < Shlwapi.h> // PathCombine, PathIsRelative
17+ #include < atomic>
18+ #include < mutex>
1719#include < sstream>
1820#include < iostream>
1921#include < string>
@@ -50,9 +52,12 @@ struct pty_baton {
5052};
5153
5254static std::vector<std::unique_ptr<pty_baton>> ptyHandles;
53- static volatile LONG ptyCounter;
55+ static std::mutex g_ptyHandlesMutex;
56+ static std::atomic<int > ptyCounter{0 };
5457
55- static pty_baton* get_pty_baton (int id) {
58+ // The leading scoped-lock parameter encodes the precondition that the caller
59+ // holds g_ptyHandlesMutex.
60+ static pty_baton* get_pty_baton (const std::lock_guard<std::mutex>&, int id) {
5661 auto it = std::find_if (ptyHandles.begin (), ptyHandles.end (), [id](const auto & ptyHandle) {
5762 return ptyHandle->id == id;
5863 });
@@ -62,17 +67,6 @@ static pty_baton* get_pty_baton(int id) {
6267 return nullptr ;
6368}
6469
65- static bool remove_pty_baton (int id) {
66- auto it = std::remove_if (ptyHandles.begin (), ptyHandles.end (), [id](const auto & ptyHandle) {
67- return ptyHandle->id == id;
68- });
69- if (it != ptyHandles.end ()) {
70- ptyHandles.erase (it);
71- return true ;
72- }
73- return false ;
74- }
75-
7670struct ExitEvent {
7771 int exit_code = 0 ;
7872};
@@ -99,11 +93,15 @@ void SetupExitCallback(Napi::Env env, Napi::Function cb, pty_baton* baton) {
9993 ExitEvent *exit_event = new ExitEvent;
10094 // Wait for process to complete.
10195 WaitForSingleObject (baton->hShell , INFINITE );
102- // Get process exit code.
103- GetExitCodeProcess (baton->hShell , (LPDWORD )(&exit_event->exit_code ));
104- // Clean up handles
105- CloseHandle (baton->hShell );
106- assert (remove_pty_baton (baton->id ));
96+ {
97+ std::lock_guard<std::mutex> lock (g_ptyHandlesMutex);
98+ GetExitCodeProcess (baton->hShell , (LPDWORD )(&exit_event->exit_code ));
99+ CloseHandle (baton->hShell );
100+ const int id = baton->id ;
101+ std::erase_if (ptyHandles, [id](const auto & ptyHandle) {
102+ return ptyHandle->id == id;
103+ });
104+ }
107105
108106 auto status = tsfn.BlockingCall (exit_event, callback); // In main thread
109107 switch (status) {
@@ -298,10 +296,13 @@ static Napi::Value PtyStartProcess(const Napi::CallbackInfo& info) {
298296
299297 if (SUCCEEDED (hr)) {
300298 // We were able to instantiate a conpty
301- const int ptyId = InterlockedIncrement (& ptyCounter) ;
299+ const int ptyId = ++ ptyCounter;
302300 marshal.Set (" pty" , Napi::Number::New (env, ptyId));
303- ptyHandles.emplace_back (
304- std::make_unique<pty_baton>(ptyId, hIn, hOut, hpc));
301+ {
302+ std::lock_guard<std::mutex> lock (g_ptyHandlesMutex);
303+ ptyHandles.emplace_back (
304+ std::make_unique<pty_baton>(ptyId, hIn, hOut, hpc));
305+ }
305306 } else {
306307 throw Napi::Error::New (env, " Cannot launch conpty" );
307308 }
@@ -349,10 +350,13 @@ static Napi::Value PtyConnect(const Napi::CallbackInfo& info) {
349350 const bool useConptyDll = info[4 ].As <Napi::Boolean>().Value ();
350351 Napi::Function exitCallback = info[5 ].As <Napi::Function>();
351352
352- // Fetch pty handle from ID and start process
353- pty_baton* handle = get_pty_baton (id);
354- if (!handle) {
355- throw Napi::Error::New (env, " Invalid pty handle" );
353+ pty_baton* handle;
354+ {
355+ std::lock_guard<std::mutex> lock (g_ptyHandlesMutex);
356+ handle = get_pty_baton (lock, id);
357+ if (!handle) {
358+ throw Napi::Error::New (env, " Invalid pty handle" );
359+ }
356360 }
357361
358362 // Prepare command line
@@ -471,7 +475,8 @@ static Napi::Value PtyResize(const Napi::CallbackInfo& info) {
471475 SHORT rows = static_cast <SHORT >(info[2 ].As <Napi::Number>().Uint32Value ());
472476 const bool useConptyDll = info[3 ].As <Napi::Boolean>().Value ();
473477
474- const pty_baton* handle = get_pty_baton (id);
478+ std::lock_guard<std::mutex> lock (g_ptyHandlesMutex);
479+ const pty_baton* handle = get_pty_baton (lock, id);
475480
476481 if (handle != nullptr ) {
477482 HANDLE hLibrary = LoadConptyDll (info, useConptyDll);
@@ -512,7 +517,8 @@ static Napi::Value PtyClear(const Napi::CallbackInfo& info) {
512517 return env.Undefined ();
513518 }
514519
515- const pty_baton* handle = get_pty_baton (id);
520+ std::lock_guard<std::mutex> lock (g_ptyHandlesMutex);
521+ const pty_baton* handle = get_pty_baton (lock, id);
516522
517523 if (handle != nullptr ) {
518524 HANDLE hLibrary = LoadConptyDll (info, useConptyDll);
@@ -543,7 +549,8 @@ static Napi::Value PtyKill(const Napi::CallbackInfo& info) {
543549 int id = info[0 ].As <Napi::Number>().Int32Value ();
544550 const bool useConptyDll = info[1 ].As <Napi::Boolean>().Value ();
545551
546- const pty_baton* handle = get_pty_baton (id);
552+ std::lock_guard<std::mutex> lock (g_ptyHandlesMutex);
553+ const pty_baton* handle = get_pty_baton (lock, id);
547554
548555 if (handle != nullptr ) {
549556 HANDLE hLibrary = LoadConptyDll (info, useConptyDll);
0 commit comments