koffi 2.6.3 → 2.6.5

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.
@@ -1426,7 +1426,7 @@ static inline void ProcessArg(const FmtArg &arg, AppendFunc append)
1426
1426
  RG_ASSERT(arg.u.random.len <= RG_SIZE(out_buf.data));
1427
1427
 
1428
1428
  for (Size j = 0; j < arg.u.random.len; j++) {
1429
- int rnd = GetRandomIntSafe(0, (int)chars.len);
1429
+ int rnd = GetRandomIntFast(0, (int)chars.len);
1430
1430
  out_buf.Append(chars[rnd]);
1431
1431
  }
1432
1432
 
@@ -2146,37 +2146,6 @@ fail:
2146
2146
  return str_buf;
2147
2147
  }
2148
2148
 
2149
- void SetEnvironmentVar(const char *name, const char *value)
2150
- {
2151
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2152
- RG_ASSERT(value);
2153
-
2154
- if (win32_utf8) {
2155
- RG_CRITICAL(SetEnvironmentVariableA(name, value), "Failed to set environment variable '%1' to '%2': %3", name, value, GetWin32ErrorString());
2156
- } else {
2157
- wchar_t name_w[256];
2158
- wchar_t value_w[4096];
2159
-
2160
- RG_CRITICAL(ConvertUtf8ToWin32Wide(name, name_w) >= 0, "Failed to set environment variable '%1' to '%2'", name, value);
2161
- RG_CRITICAL(ConvertUtf8ToWin32Wide(value, value_w) >= 0, "Failed to set environment variable '%1' to '%2'", name, value);
2162
- RG_CRITICAL(SetEnvironmentVariableW(name_w, value_w), "Failed to set environment variable '%1' to '%2': %3", name, value, GetWin32ErrorString());
2163
- }
2164
- }
2165
-
2166
- void DeleteEnvironmentVar(const char *name)
2167
- {
2168
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2169
-
2170
- if (win32_utf8) {
2171
- RG_CRITICAL(SetEnvironmentVariableA(name, nullptr), "Failed to clear environment variable '%1': %2", name, GetWin32ErrorString());
2172
- } else {
2173
- wchar_t name_w[256];
2174
-
2175
- RG_CRITICAL(ConvertUtf8ToWin32Wide(name, name_w) >= 0, "Failed to clear environment variable '%1'", name);
2176
- RG_CRITICAL(SetEnvironmentVariableW(name_w, nullptr), "Failed to clear environment variable '%1': %2", name, GetWin32ErrorString());
2177
- }
2178
- }
2179
-
2180
2149
  static FileType FileAttributesToType(uint32_t attr)
2181
2150
  {
2182
2151
  if (attr & FILE_ATTRIBUTE_DIRECTORY) {
@@ -2360,20 +2329,6 @@ EnumResult EnumerateDirectory(const char *dirname, const char *filter, Size max_
2360
2329
 
2361
2330
  #else
2362
2331
 
2363
- void SetEnvironmentVar(const char *name, const char *value)
2364
- {
2365
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2366
- RG_ASSERT(value);
2367
-
2368
- RG_CRITICAL(!setenv(name, value, 1), "Failed to set environment variable '%1' to '%2': %3", name, value, strerror(errno));
2369
- }
2370
-
2371
- void DeleteEnvironmentVar(const char *name)
2372
- {
2373
- RG_ASSERT(name && name[0] && !strchr(name, '='));
2374
- RG_CRITICAL(!unsetenv(name), "Failed to clear environment variable '%1': %2", name, strerror(errno));
2375
- }
2376
-
2377
2332
  static FileType FileModeToType(mode_t mode)
2378
2333
  {
2379
2334
  if (S_ISDIR(mode)) {
@@ -2473,11 +2428,15 @@ StatResult StatFile(const char *filename, unsigned int flags, FileInfo *out_info
2473
2428
  (int64_t)sb.st_mtim.tv_nsec / 1000000;
2474
2429
  out_info->btime = (int64_t)sb.__st_birthtim.tv_sec * 1000 +
2475
2430
  (int64_t)sb.__st_birthtim.tv_nsec / 1000000;
2476
- #else
2431
+ #elif defined(__FreeBSD__)
2477
2432
  out_info->mtime = (int64_t)sb.st_mtim.tv_sec * 1000 +
2478
2433
  (int64_t)sb.st_mtim.tv_nsec / 1000000;
2479
2434
  out_info->btime = (int64_t)sb.st_birthtim.tv_sec * 1000 +
2480
2435
  (int64_t)sb.st_birthtim.tv_nsec / 1000000;
2436
+ #else
2437
+ out_info->mtime = (int64_t)sb.st_mtim.tv_sec * 1000 +
2438
+ (int64_t)sb.st_mtim.tv_nsec / 1000000;
2439
+ out_info->btime = out_info->mtime;
2481
2440
  #endif
2482
2441
  out_info->mode = (unsigned int)sb.st_mode;
2483
2442
  out_info->uid = (uint32_t)sb.st_uid;
@@ -3919,7 +3878,7 @@ struct PendingIO {
3919
3878
  }
3920
3879
  };
3921
3880
 
3922
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3881
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
3923
3882
  FunctionRef<Span<const uint8_t>()> in_func,
3924
3883
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
3925
3884
  {
@@ -3934,9 +3893,9 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3934
3893
 
3935
3894
  // Convert work directory
3936
3895
  Span<wchar_t> work_dir_w;
3937
- if (work_dir) {
3938
- work_dir_w = AllocateSpan<wchar_t>(&temp_alloc, 2 * strlen(work_dir) + 1);
3939
- if (ConvertUtf8ToWin32Wide(work_dir, work_dir_w) < 0)
3896
+ if (info.work_dir) {
3897
+ work_dir_w = AllocateSpan<wchar_t>(&temp_alloc, 2 * strlen(info.work_dir) + 1);
3898
+ if (ConvertUtf8ToWin32Wide(info.work_dir, work_dir_w) < 0)
3940
3899
  return false;
3941
3900
  } else {
3942
3901
  work_dir_w = {};
@@ -3988,6 +3947,45 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
3988
3947
  if (out_func.IsValid() && !CreateOverlappedPipe(true, false, PipeMode::Byte, out_pipe))
3989
3948
  return false;
3990
3949
 
3950
+ // Prepare environment (if needed)
3951
+ HeapArray<wchar_t> new_env_w;
3952
+ if (info.reset_env || info.env_variables.len) {
3953
+ if (!info.reset_env) {
3954
+ Span<wchar_t> current_env = MakeSpan(GetEnvironmentStringsW(), 0);
3955
+
3956
+ do {
3957
+ Size len = (Size)wcslen(current_env.end());
3958
+ current_env.len += len + 1;
3959
+ } while (current_env.ptr[current_env.len]);
3960
+
3961
+ new_env_w.Append(current_env);
3962
+ }
3963
+
3964
+ for (const ExecuteInfo::KeyValue &kv: info.env_variables) {
3965
+ Span<const char> key = kv.key;
3966
+ Span<const char> value = kv.value;
3967
+
3968
+ Size len = 2 * (key.len + value.len + 1) + 1;
3969
+ new_env_w.Reserve(len);
3970
+
3971
+ len = ConvertUtf8ToWin32Wide(key, new_env_w.TakeAvailable());
3972
+ if (len < 0) [[unlikely]]
3973
+ return false;
3974
+ new_env_w.len += len;
3975
+
3976
+ new_env_w.Append(L'=');
3977
+
3978
+ len = ConvertUtf8ToWin32Wide(value, new_env_w.TakeAvailable());
3979
+ if (len < 0) [[unlikely]]
3980
+ return false;
3981
+ new_env_w.len += len;
3982
+
3983
+ new_env_w.Append(0);
3984
+ }
3985
+
3986
+ new_env_w.Append(0);
3987
+ }
3988
+
3991
3989
  // Start process
3992
3990
  HANDLE process_handle;
3993
3991
  {
@@ -4012,9 +4010,11 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4012
4010
  si.dwFlags |= STARTF_USESTDHANDLES;
4013
4011
  }
4014
4012
 
4013
+ int flags = CREATE_NEW_PROCESS_GROUP | CREATE_UNICODE_ENVIRONMENT;
4014
+
4015
4015
  PROCESS_INFORMATION pi = {};
4016
- if (!CreateProcessW(nullptr, cmd_line_w.ptr, nullptr, nullptr, TRUE, CREATE_NEW_PROCESS_GROUP,
4017
- nullptr, work_dir_w.ptr, &si, &pi)) {
4016
+ if (!CreateProcessW(nullptr, cmd_line_w.ptr, nullptr, nullptr, TRUE, flags,
4017
+ new_env_w.ptr, work_dir_w.ptr, &si, &pi)) {
4018
4018
  LogError("Failed to start process: %1", GetWin32ErrorString());
4019
4019
  return false;
4020
4020
  }
@@ -4223,10 +4223,12 @@ void CloseDescriptorSafe(int *fd_ptr)
4223
4223
  *fd_ptr = -1;
4224
4224
  }
4225
4225
 
4226
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4226
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4227
4227
  FunctionRef<Span<const uint8_t>()> in_func,
4228
4228
  FunctionRef<void(Span<uint8_t> buf)> out_func, int *out_code)
4229
4229
  {
4230
+ BlockAllocator temp_alloc;
4231
+
4230
4232
  // Create read pipes
4231
4233
  int in_pfd[2] = {-1, -1};
4232
4234
  RG_DEFER {
@@ -4277,6 +4279,26 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4277
4279
  }
4278
4280
  }
4279
4281
 
4282
+ // Prepare new environment (if needed)
4283
+ HeapArray<char *> new_env;
4284
+ if (info.reset_env || info.env_variables.len) {
4285
+ if (!info.reset_env) {
4286
+ char **ptr = environ;
4287
+
4288
+ while (*ptr) {
4289
+ new_env.Append(*ptr);
4290
+ ptr++;
4291
+ }
4292
+ }
4293
+
4294
+ for (const ExecuteInfo::KeyValue &kv: info.env_variables) {
4295
+ const char *var = Fmt(&temp_alloc, "%1=%2", kv.key, kv.value).ptr;
4296
+ new_env.Append((char *)var);
4297
+ }
4298
+
4299
+ new_env.Append(nullptr);
4300
+ }
4301
+
4280
4302
  // Start process
4281
4303
  pid_t pid;
4282
4304
  {
@@ -4297,12 +4319,12 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4297
4319
  return false;
4298
4320
  }
4299
4321
 
4300
- if (work_dir) {
4301
- const char *argv[] = {"env", "-C", work_dir, "sh", "-c", cmd_line, nullptr };
4302
- errno = posix_spawn(&pid, "/bin/env", &file_actions, nullptr, const_cast<char **>(argv), environ);
4322
+ if (info.work_dir) {
4323
+ const char *argv[] = {"env", "-C", info.work_dir, "sh", "-c", cmd_line, nullptr };
4324
+ errno = posix_spawn(&pid, "/bin/env", &file_actions, nullptr, const_cast<char **>(argv), new_env.ptr ? new_env.ptr : environ);
4303
4325
  } else {
4304
4326
  const char *argv[] = {"sh", "-c", cmd_line, nullptr};
4305
- errno = posix_spawn(&pid, "/bin/sh", &file_actions, nullptr, const_cast<char **>(argv), environ);
4327
+ errno = posix_spawn(&pid, "/bin/sh", &file_actions, nullptr, const_cast<char **>(argv), new_env.ptr ? new_env.ptr : environ);
4306
4328
  }
4307
4329
  if (errno) {
4308
4330
  LogError("Failed to start process: %1", strerror(errno));
@@ -4444,7 +4466,7 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4444
4466
 
4445
4467
  #endif
4446
4468
 
4447
- bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4469
+ bool ExecuteCommandLine(const char *cmd_line, const ExecuteInfo &info,
4448
4470
  Span<const uint8_t> in_buf, Size max_len,
4449
4471
  HeapArray<uint8_t> *out_buf, int *out_code)
4450
4472
  {
@@ -4467,8 +4489,8 @@ bool ExecuteCommandLine(const char *cmd_line, const char *work_dir,
4467
4489
  // Don't f*ck up the log
4468
4490
  bool warned = false;
4469
4491
 
4470
- bool success = ExecuteCommandLine(cmd_line, work_dir, [&]() { return in_buf; },
4471
- [&](Span<uint8_t> buf) {
4492
+ bool success = ExecuteCommandLine(cmd_line, info, [&]() { return in_buf; },
4493
+ [&](Span<uint8_t> buf) {
4472
4494
  if (out_buf->len - start_len <= max_len - buf.len) {
4473
4495
  out_buf->Append(buf);
4474
4496
  } else if (!warned) {
@@ -4494,7 +4516,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
4494
4516
  };
4495
4517
 
4496
4518
  int exit_code;
4497
- if (!ExecuteCommandLine(cmd_line, nullptr, MakeSpan((const uint8_t *)nullptr, 0), write, &exit_code))
4519
+ if (!ExecuteCommandLine(cmd_line, {}, MakeSpan((const uint8_t *)nullptr, 0), write, &exit_code))
4498
4520
  return -1;
4499
4521
  if (exit_code) {
4500
4522
  LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
@@ -4507,7 +4529,7 @@ Size ReadCommandOutput(const char *cmd_line, Span<char> out_output)
4507
4529
  bool ReadCommandOutput(const char *cmd_line, HeapArray<char> *out_output)
4508
4530
  {
4509
4531
  int exit_code;
4510
- if (!ExecuteCommandLine(cmd_line, nullptr, {}, Mebibytes(1), out_output, &exit_code))
4532
+ if (!ExecuteCommandLine(cmd_line, {}, {}, Mebibytes(1), out_output, &exit_code))
4511
4533
  return false;
4512
4534
  if (exit_code) {
4513
4535
  LogDebug("Command '%1 failed (exit code: %2)", cmd_line, exit_code);
@@ -4759,7 +4781,7 @@ bool NotifySystemd()
4759
4781
  return false;
4760
4782
  }
4761
4783
 
4762
- DeleteEnvironmentVar("NOTIFY_SOCKET");
4784
+ unsetenv("NOTIFY_SOCKET");
4763
4785
  return true;
4764
4786
  }
4765
4787
  #endif
@@ -5063,79 +5085,6 @@ const char *CreateUniqueDirectory(Span<const char> directory, const char *prefix
5063
5085
  // Random
5064
5086
  // ------------------------------------------------------------------------
5065
5087
 
5066
- static inline uint32_t ROTL32(uint32_t v, int n)
5067
- {
5068
- return (v << n) | (v >> (32 - n));
5069
- }
5070
-
5071
- static inline uint64_t ROTL64(uint64_t v, int n) {
5072
- return (v << n) | (v >> (64 - n));
5073
- }
5074
-
5075
- FastRandom::FastRandom()
5076
- {
5077
- do {
5078
- FillRandomSafe(state, RG_SIZE(state));
5079
- } while (std::all_of(std::begin(state), std::end(state), [](uint64_t v) { return !v; }));
5080
- }
5081
-
5082
- FastRandom::FastRandom(uint64_t seed)
5083
- {
5084
- // splitmix64 generator to seed xoshiro256++, as recommended
5085
-
5086
- seed += 0x9e3779b97f4a7c15;
5087
-
5088
- for (int i = 0; i < 4; i++) {
5089
- seed = (seed ^ (seed >> 30)) * 0xbf58476d1ce4e5b9;
5090
- seed = (seed ^ (seed >> 27)) * 0x94d049bb133111eb;
5091
- state[i] = seed ^ (seed >> 31);
5092
- }
5093
- }
5094
-
5095
- void FastRandom::Fill(void *out_buf, Size len)
5096
- {
5097
- for (Size i = 0; i < len; i += 8) {
5098
- uint64_t rnd = Next();
5099
-
5100
- Size copy_len = std::min(RG_SIZE(rnd), len - i);
5101
- memcpy((uint8_t *)out_buf + i, &rnd, copy_len);
5102
- }
5103
- }
5104
-
5105
- int FastRandom::GetInt(int min, int max)
5106
- {
5107
- int range = max - min;
5108
- RG_ASSERT(range >= 2);
5109
-
5110
- unsigned int treshold = (UINT_MAX - UINT_MAX % range);
5111
-
5112
- unsigned int x;
5113
- do {
5114
- Fill(&x, RG_SIZE(x));
5115
- } while (x >= treshold);
5116
- x %= range;
5117
-
5118
- return min + (int)x;
5119
- }
5120
-
5121
- uint64_t FastRandom::Next()
5122
- {
5123
- // xoshiro256++ by David Blackman and Sebastiano Vigna (vigna@acm.org)
5124
- // Hopefully I did not screw it up :)
5125
-
5126
- uint64_t result = ROTL64(state[0] + state[3], 23) + state[0];
5127
- uint64_t t = state[1] << 17;
5128
-
5129
- state[2] ^= state[0];
5130
- state[3] ^= state[1];
5131
- state[1] ^= state[2];
5132
- state[0] ^= state[3];
5133
- state[2] ^= t;
5134
- state[3] = ROTL64(state[3], 45);
5135
-
5136
- return result;
5137
- }
5138
-
5139
5088
  static RG_THREAD_LOCAL Size rnd_remain;
5140
5089
  static RG_THREAD_LOCAL int64_t rnd_time;
5141
5090
  #ifndef _WIN32
@@ -5145,6 +5094,17 @@ static RG_THREAD_LOCAL uint32_t rnd_state[16];
5145
5094
  static RG_THREAD_LOCAL uint8_t rnd_buf[64];
5146
5095
  static RG_THREAD_LOCAL Size rnd_offset;
5147
5096
 
5097
+ static thread_local FastRandom rng_fast;
5098
+
5099
+ static inline uint32_t ROTL32(uint32_t v, int n)
5100
+ {
5101
+ return (v << n) | (v >> (32 - n));
5102
+ }
5103
+
5104
+ static inline uint64_t ROTL64(uint64_t v, int n) {
5105
+ return (v << n) | (v >> (64 - n));
5106
+ }
5107
+
5148
5108
  static void InitChaCha20(uint32_t state[16], uint32_t key[8], uint32_t iv[2])
5149
5109
  {
5150
5110
  alignas(uint32_t) static char str[] = "expand 32-byte k";
@@ -5251,18 +5211,18 @@ void FillRandomSafe(void *out_buf, Size len)
5251
5211
 
5252
5212
  memset(rnd_state, 0, RG_SIZE(rnd_state));
5253
5213
  #if defined(_WIN32)
5254
- RG_CRITICAL(RtlGenRandom(&buf, RG_SIZE(buf)), "RtlGenRandom() failed: %s", GetWin32ErrorString());
5214
+ RG_CRITICAL(RtlGenRandom(&buf, RG_SIZE(buf)), "RtlGenRandom() failed: %1", GetWin32ErrorString());
5255
5215
  #elif defined(__linux__)
5256
5216
  {
5257
5217
  restart:
5258
5218
  int ret = syscall(SYS_getrandom, &buf, RG_SIZE(buf), 0);
5259
- RG_CRITICAL(ret >= 0, "getentropy() failed: %s", strerror(errno));
5219
+ RG_CRITICAL(ret >= 0, "getentropy() failed: %1", strerror(errno));
5260
5220
 
5261
5221
  if (ret < RG_SIZE(buf))
5262
5222
  goto restart;
5263
5223
  }
5264
5224
  #else
5265
- RG_CRITICAL(getentropy(&buf, RG_SIZE(buf)) == 0, "getentropy() failed: %s", strerror(errno));
5225
+ RG_CRITICAL(getentropy(&buf, RG_SIZE(buf)) == 0, "getentropy() failed: %1", strerror(errno));
5266
5226
  #endif
5267
5227
 
5268
5228
  InitChaCha20(rnd_state, buf.key, buf.iv);
@@ -5312,6 +5272,75 @@ int GetRandomIntSafe(int min, int max)
5312
5272
  return min + (int)x;
5313
5273
  }
5314
5274
 
5275
+ FastRandom::FastRandom()
5276
+ {
5277
+ do {
5278
+ FillRandomSafe(state, RG_SIZE(state));
5279
+ } while (std::all_of(std::begin(state), std::end(state), [](uint64_t v) { return !v; }));
5280
+ }
5281
+
5282
+ FastRandom::FastRandom(uint64_t seed)
5283
+ {
5284
+ // splitmix64 generator to seed xoshiro256++, as recommended
5285
+
5286
+ seed += 0x9e3779b97f4a7c15;
5287
+
5288
+ for (int i = 0; i < 4; i++) {
5289
+ seed = (seed ^ (seed >> 30)) * 0xbf58476d1ce4e5b9;
5290
+ seed = (seed ^ (seed >> 27)) * 0x94d049bb133111eb;
5291
+ state[i] = seed ^ (seed >> 31);
5292
+ }
5293
+ }
5294
+
5295
+ void FastRandom::Fill(void *out_buf, Size len)
5296
+ {
5297
+ for (Size i = 0; i < len; i += 8) {
5298
+ uint64_t rnd = Next();
5299
+
5300
+ Size copy_len = std::min(RG_SIZE(rnd), len - i);
5301
+ memcpy((uint8_t *)out_buf + i, &rnd, copy_len);
5302
+ }
5303
+ }
5304
+
5305
+ int FastRandom::GetInt(int min, int max)
5306
+ {
5307
+ int range = max - min;
5308
+ RG_ASSERT(range >= 2);
5309
+
5310
+ unsigned int treshold = (UINT_MAX - UINT_MAX % range);
5311
+
5312
+ unsigned int x;
5313
+ do {
5314
+ x = (unsigned int)Next();
5315
+ } while (x >= treshold);
5316
+ x %= range;
5317
+
5318
+ return min + (int)x;
5319
+ }
5320
+
5321
+ uint64_t FastRandom::Next()
5322
+ {
5323
+ // xoshiro256++ by David Blackman and Sebastiano Vigna (vigna@acm.org)
5324
+ // Hopefully I did not screw it up :)
5325
+
5326
+ uint64_t result = ROTL64(state[0] + state[3], 23) + state[0];
5327
+ uint64_t t = state[1] << 17;
5328
+
5329
+ state[2] ^= state[0];
5330
+ state[3] ^= state[1];
5331
+ state[1] ^= state[2];
5332
+ state[0] ^= state[3];
5333
+ state[2] ^= t;
5334
+ state[3] = ROTL64(state[3], 45);
5335
+
5336
+ return result;
5337
+ }
5338
+
5339
+ int GetRandomIntFast(int min, int max)
5340
+ {
5341
+ return rng_fast.GetInt(min, max);
5342
+ }
5343
+
5315
5344
  // ------------------------------------------------------------------------
5316
5345
  // Sockets
5317
5346
  // ------------------------------------------------------------------------
@@ -5517,7 +5546,10 @@ struct Task {
5517
5546
  std::function<bool()> func;
5518
5547
  };
5519
5548
 
5520
- struct TaskQueue {
5549
+ struct WorkerData {
5550
+ AsyncPool *pool = nullptr;
5551
+ int idx;
5552
+
5521
5553
  std::mutex queue_mutex;
5522
5554
  BucketArray<Task> tasks;
5523
5555
  };
@@ -5533,15 +5565,13 @@ class AsyncPool {
5533
5565
  int refcount = 0;
5534
5566
 
5535
5567
  int async_count = 0;
5536
- HeapArray<bool> workers_state;
5537
-
5538
- HeapArray<TaskQueue> queues;
5568
+ HeapArray<WorkerData> workers;
5539
5569
  std::atomic_int pending_tasks { 0 };
5540
5570
 
5541
5571
  public:
5542
5572
  AsyncPool(int threads, bool leak);
5543
5573
 
5544
- int GetWorkerCount() const { return (int)queues.len; }
5574
+ int GetWorkerCount() const { return (int)workers.len; }
5545
5575
  int CountPendingTasks() const { return pending_tasks; }
5546
5576
 
5547
5577
  void RegisterAsync();
@@ -5552,7 +5582,7 @@ public:
5552
5582
  void RunWorker(int worker_idx);
5553
5583
  void SyncOn(Async *async);
5554
5584
 
5555
- void RunTasks(int queue_idx);
5585
+ void RunTasks(int worker_idx);
5556
5586
  void RunTask(Task *task);
5557
5587
  };
5558
5588
 
@@ -5643,25 +5673,72 @@ AsyncPool::AsyncPool(int threads, bool leak)
5643
5673
  threads = RG_ASYNC_MAX_THREADS;
5644
5674
  }
5645
5675
 
5646
- // The first queue is for the main thread, whereas workers_state[0] is
5647
- // not used but it's easier to index it the same way.
5648
- workers_state.AppendDefault(threads);
5649
- queues.AppendDefault(threads);
5676
+ // The first queue is for the main thread
5677
+ workers.AppendDefault(threads);
5650
5678
 
5651
5679
  refcount = leak;
5652
5680
  }
5653
5681
 
5682
+ #ifdef _WIN32
5683
+
5684
+ static DWORD WINAPI RunWorkerWin32(void *udata)
5685
+ {
5686
+ WorkerData *worker = (WorkerData *)udata;
5687
+ worker->pool->RunWorker(worker->idx);
5688
+ return 0;
5689
+ }
5690
+
5691
+ #else
5692
+
5693
+ static void *RunWorkerPthread(void *udata)
5694
+ {
5695
+ WorkerData *worker = (WorkerData *)udata;
5696
+ worker->pool->RunWorker(worker->idx);
5697
+ return nullptr;
5698
+ }
5699
+
5700
+ #endif
5701
+
5654
5702
  void AsyncPool::RegisterAsync()
5655
5703
  {
5656
5704
  std::lock_guard<std::mutex> lock_pool(pool_mutex);
5657
5705
 
5658
5706
  if (!async_count++) {
5659
- for (int i = 1; i < workers_state.len; i++) {
5660
- if (!workers_state[i]) {
5661
- std::thread(&AsyncPool::RunWorker, this, i).detach();
5707
+ for (int i = 1; i < workers.len; i++) {
5708
+ WorkerData *worker = &workers[i];
5709
+
5710
+ if (!worker->pool) {
5711
+ worker->pool = this;
5712
+ worker->idx = i;
5713
+
5714
+ #ifdef _WIN32
5715
+ // Our worker threads may exit after main() has returned (or exit has been called),
5716
+ // which can trigger crashes in _Cnd_do_broadcast_at_thread_exit() because it
5717
+ // tries to dereference destroyed stuff. It turns out that std::thread calls this
5718
+ // function, and we don't want that, so avoid std::thread on Windows.
5719
+ HANDLE h = CreateThread(nullptr, 0, RunWorkerWin32, worker, 0, nullptr);
5720
+ if (!h) [[unlikely]] {
5721
+ LogError("Failed to create worker thread: %1", GetWin32ErrorString());
5722
+
5723
+ worker->pool = nullptr;
5724
+ return;
5725
+ }
5726
+
5727
+ CloseHandle(h);
5728
+ #else
5729
+ pthread_t thread;
5730
+ int ret = pthread_create(&thread, nullptr, RunWorkerPthread, worker);
5731
+ if (ret) [[unlikely]] {
5732
+ LogError("Failed to create worker thread: %1", strerror(ret));
5733
+
5734
+ worker->pool = nullptr;
5735
+ return;
5736
+ }
5737
+
5738
+ pthread_detach(thread);
5739
+ #endif
5662
5740
 
5663
5741
  refcount++;
5664
- workers_state[i] = true;
5665
5742
  }
5666
5743
  }
5667
5744
  }
@@ -5677,20 +5754,20 @@ void AsyncPool::AddTask(Async *async, const std::function<bool()> &func)
5677
5754
  {
5678
5755
  if (async_running_pool != async->pool) {
5679
5756
  for (;;) {
5680
- int idx = GetRandomIntSafe(0, queues.len);
5681
- TaskQueue *queue = &queues[idx];
5757
+ int idx = GetRandomIntFast(0, (int)workers.len);
5758
+ WorkerData *worker = &workers[idx];
5682
5759
 
5683
- std::unique_lock<std::mutex> lock_queue(queue->queue_mutex, std::try_to_lock);
5760
+ std::unique_lock<std::mutex> lock_queue(worker->queue_mutex, std::try_to_lock);
5684
5761
  if (lock_queue.owns_lock()) {
5685
- queue->tasks.Append({ async, func });
5762
+ worker->tasks.Append({ async, func });
5686
5763
  break;
5687
5764
  }
5688
5765
  }
5689
5766
  } else {
5690
- TaskQueue *queue = &queues[async_running_worker_idx];
5767
+ WorkerData *worker = &workers[async_running_worker_idx];
5691
5768
 
5692
- std::lock_guard<std::mutex> lock_queue(queue->queue_mutex);
5693
- queue->tasks.Append({ async, func });
5769
+ std::lock_guard<std::mutex> lock_queue(worker->queue_mutex);
5770
+ worker->tasks.Append({ async, func });
5694
5771
  }
5695
5772
 
5696
5773
  async->remaining_tasks++;
@@ -5720,7 +5797,8 @@ void AsyncPool::RunWorker(int worker_idx)
5720
5797
  pending_cv.wait_for(lock_pool, duration, [&]() { return !!pending_tasks; });
5721
5798
  }
5722
5799
 
5723
- workers_state[worker_idx] = false;
5800
+ workers[worker_idx].pool = nullptr;
5801
+
5724
5802
  if (!--refcount) {
5725
5803
  lock_pool.unlock();
5726
5804
  delete this;
@@ -5739,31 +5817,31 @@ void AsyncPool::SyncOn(Async *async)
5739
5817
  async_running_worker_idx = 0;
5740
5818
 
5741
5819
  while (async->remaining_tasks) {
5742
- RunTasks(async_running_worker_idx);
5820
+ RunTasks(0);
5743
5821
 
5744
5822
  std::unique_lock<std::mutex> lock_sync(pool_mutex);
5745
5823
  sync_cv.wait(lock_sync, [&]() { return pending_tasks || !async->remaining_tasks; });
5746
5824
  }
5747
5825
  }
5748
5826
 
5749
- void AsyncPool::RunTasks(int queue_idx)
5827
+ void AsyncPool::RunTasks(int worker_idx)
5750
5828
  {
5751
5829
  // The '12' factor is pretty arbitrary, don't try to find meaning there
5752
- for (int i = 0; i < workers_state.len * 12; i++) {
5753
- TaskQueue *queue = &queues[queue_idx];
5754
- std::unique_lock<std::mutex> lock_queue(queue->queue_mutex, std::try_to_lock);
5830
+ for (int i = 0; i < workers.len * 12; i++) {
5831
+ WorkerData *worker = &workers[worker_idx];
5832
+ std::unique_lock<std::mutex> lock_queue(worker->queue_mutex, std::try_to_lock);
5755
5833
 
5756
- if (lock_queue.owns_lock() && queue->tasks.len) {
5757
- Task task = std::move(queue->tasks[0]);
5834
+ if (lock_queue.owns_lock() && worker->tasks.len) {
5835
+ Task task = std::move(worker->tasks[0]);
5758
5836
 
5759
- queue->tasks.RemoveFirst();
5760
- queue->tasks.Trim();
5837
+ worker->tasks.RemoveFirst();
5838
+ worker->tasks.Trim();
5761
5839
 
5762
5840
  lock_queue.unlock();
5763
5841
 
5764
5842
  RunTask(&task);
5765
5843
  } else {
5766
- queue_idx = (++queue_idx < queues.len) ? queue_idx : 0;
5844
+ worker_idx = (++worker_idx < workers.len) ? worker_idx : 0;
5767
5845
  }
5768
5846
  }
5769
5847
  }
@@ -5982,10 +6060,16 @@ void Fiber::FiberCallback(unsigned int high, unsigned int low)
5982
6060
  static RG_THREAD_LOCAL std::unique_lock<std::mutex> *fib_lock;
5983
6061
  static RG_THREAD_LOCAL Fiber *fib_self;
5984
6062
 
5985
- Fiber::Fiber(const std::function<bool()> &f, Size stack_size)
6063
+ Fiber::Fiber(const std::function<bool()> &f, Size)
5986
6064
  : f(f)
5987
6065
  {
5988
- thread = std::thread(ThreadCallback, this);
6066
+ int ret = pthread_create(&thread, nullptr, ThreadCallback, this);
6067
+ if (ret) {
6068
+ LogError("Failed to create thread: %1", strerror(ret));
6069
+ return;
6070
+ }
6071
+ joinable = true;
6072
+
5989
6073
  done = false;
5990
6074
 
5991
6075
  while (toggle == 1) {
@@ -5998,8 +6082,8 @@ Fiber::~Fiber()
5998
6082
  // We are forced to execute it until the end
5999
6083
  Finalize();
6000
6084
 
6001
- if (thread.joinable()) {
6002
- thread.join();
6085
+ if (joinable) {
6086
+ pthread_join(thread, nullptr);
6003
6087
  }
6004
6088
  }
6005
6089
 
@@ -6030,7 +6114,7 @@ bool Fiber::SwitchBack()
6030
6114
  }
6031
6115
  }
6032
6116
 
6033
- void Fiber::ThreadCallback(void *udata)
6117
+ void *Fiber::ThreadCallback(void *udata)
6034
6118
  {
6035
6119
  Fiber *self = (Fiber *)udata;
6036
6120
 
@@ -6047,6 +6131,8 @@ void Fiber::ThreadCallback(void *udata)
6047
6131
 
6048
6132
  self->toggle = 0;
6049
6133
  self->cv.notify_one();
6134
+
6135
+ return nullptr;
6050
6136
  }
6051
6137
 
6052
6138
  void Fiber::Toggle(int to, std::unique_lock<std::mutex> *lock)