Skip to content

Commit 81be79c

Browse files
fix: setTimeout does not work on Windows
As the pthread_cancel function is not available on Windows, change the definition of setTimeout method - from now on when you want to use it, you should pass the timeout, the data that you want to be passed to the function that will be executed in case the timeout passes and the function itself. The setTimeout method automatically allocates memory for the objects, so they will still live even when the function from which you call the setTimeout finishes its execution. It is not a good idea to capture variables (with lambda functions) when we'll use them in a separate thread. As we do not await the execution of the thread, the captured variables may be cleared (method from which we've called setTimeout finished its execution). More information is available here http://bannalia.blogspot.com/2016/07/passing-capturing-c-lambda-functions-as.html NOTE: Changes are not handled for macOS. NOTE: Allocated memory should be cleaned in clearTimeout as well.
1 parent 0cbc1ad commit 81be79c

File tree

1 file changed

+67
-11
lines changed

1 file changed

+67
-11
lines changed

IOSDeviceLib/SocketHelper.cpp

+67-11
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,93 @@
77
#include "PlistCpp/Plist.hpp"
88
#include "PlistCpp/PlistDate.hpp"
99

10+
struct TimeoutData {
11+
int timeout;
12+
void * data;
13+
void(*operation)(void *);
14+
};
15+
16+
#ifdef WIN32
17+
DWORD WINAPI TimeoutThreadExecutor(LPVOID lpParam) {
18+
TimeoutData* threadData = (TimeoutData*)lpParam;
19+
std::this_thread::sleep_for(std::chrono::milliseconds(threadData->timeout * 1000));
20+
threadData->operation(threadData->data);
21+
free(threadData);
22+
return 0;
23+
}
24+
25+
HANDLE setTimeout(int timeout, void * data, void(*operation)(void*))
26+
{
27+
struct TimeoutData* currentTimeoutDataPtr = reinterpret_cast<TimeoutData*>(malloc(sizeof(struct TimeoutData)));
28+
currentTimeoutDataPtr->timeout = timeout;
29+
currentTimeoutDataPtr->data = data;
30+
currentTimeoutDataPtr->operation = operation;
31+
32+
auto threadId = CreateThread(
33+
NULL, // default security attributes
34+
0, // use default stack size
35+
TimeoutThreadExecutor, // thread function name
36+
currentTimeoutDataPtr, // argument to thread function
37+
0, // use default creation flags
38+
nullptr);
39+
40+
return threadId;
41+
}
42+
#else
1043
std::thread::native_handle_type setTimeout(std::function<void()> operation, int timeout) {
1144
std::thread thread = std::thread([=]() {
1245
std::this_thread::sleep_for(std::chrono::milliseconds(timeout * 1000));
1346
operation();
1447
});
15-
48+
1649
std::thread::native_handle_type native = thread.native_handle();
1750
thread.detach();
18-
51+
1952
return native;
2053
}
2154

22-
void clearTimeout(std::thread::native_handle_type nativeHandle) {
55+
#endif
56+
57+
void clearTimeout(void* nativeHandle) {
58+
// TODO: clean the malloced data
59+
2360
if (nativeHandle) {
61+
#ifdef WIN32
62+
int terminateThreadResult = TerminateThread(nativeHandle, 9);
63+
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-terminatethread#return-value
64+
if (terminateThreadResult == 0)
65+
{
66+
DWORD errorCode = GetLastError();
67+
}
68+
#else
2469
pthread_cancel(nativeHandle);
70+
#endif
2571
}
2672
}
27-
73+
74+
void CleanConnectionResources(void * data)
75+
{
76+
// TODO: Remove the connection from the dictionary of available connections for current device.
77+
// TODO: Use mutex for invalidating the connection, someone might use it.
78+
ServiceConnRef conn = reinterpret_cast<ServiceConnRef>(data);
79+
AMDServiceConnectionInvalidate(conn);
80+
}
81+
2882
std::mutex receive_con_message_mutex;
2983
std::map<std::string, boost::any> receive_con_message(ServiceConnRef con, std::string device_identifier, std::string method_id, int timeout)
3084
{
3185
receive_con_message_mutex.lock();
3286

33-
std::thread::native_handle_type nativeHandle;
34-
87+
std::thread::native_handle_type nativeHandle = nullptr;
88+
3589
if (timeout > 0) {
36-
nativeHandle = setTimeout([=]() {
37-
AMDServiceConnectionInvalidate(con);
38-
}, timeout);
90+
nativeHandle = setTimeout(timeout, con, CleanConnectionResources);
3991
}
40-
92+
4193
std::map<std::string, boost::any> dict;
4294
char *buffer = new char[4];
4395
int bytes_read = AMDServiceConnectionReceive(con, buffer, 4);
96+
// TODO: clearTimeout in case connection is alive, but there's nothing to read, i.e. we have 0 bytes for reading.
4497
if (bytes_read > 0)
4598
{
4699
unsigned long res = ntohl(*((unsigned long*)buffer));
@@ -50,7 +103,10 @@ std::map<std::string, boost::any> receive_con_message(ServiceConnRef con, std::s
50103
if (bytes_read > 0)
51104
{
52105
Plist::readPlist(buffer, res, dict);
53-
clearTimeout(nativeHandle);
106+
if (nativeHandle)
107+
{
108+
clearTimeout(nativeHandle);
109+
}
54110
}
55111
}
56112

0 commit comments

Comments
 (0)