Coverage Report

Created: 2025-12-30 08:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/node/src/node_worker.cc
Line
Count
Source
1
#include "node_worker.h"
2
#include "async_wrap-inl.h"
3
#include "debug_utils-inl.h"
4
#include "histogram-inl.h"
5
#include "memory_tracker-inl.h"
6
#include "node_buffer.h"
7
#include "node_errors.h"
8
#include "node_external_reference.h"
9
#include "node_options-inl.h"
10
#include "node_perf.h"
11
#include "node_snapshot_builder.h"
12
#include "permission/permission.h"
13
#include "util-inl.h"
14
#include "v8-cppgc.h"
15
#include "v8-profiler.h"
16
17
#include <memory>
18
#include <string>
19
#include <vector>
20
21
using node::kAllowedInEnvvar;
22
using node::kDisallowedInEnvvar;
23
using v8::AllocationProfile;
24
using v8::Array;
25
using v8::ArrayBuffer;
26
using v8::Boolean;
27
using v8::Context;
28
using v8::CpuProfile;
29
using v8::CpuProfilingResult;
30
using v8::CpuProfilingStatus;
31
using v8::DictionaryTemplate;
32
using v8::Float64Array;
33
using v8::FunctionCallbackInfo;
34
using v8::FunctionTemplate;
35
using v8::HandleScope;
36
using v8::HeapProfiler;
37
using v8::HeapStatistics;
38
using v8::Integer;
39
using v8::Isolate;
40
using v8::Local;
41
using v8::Locker;
42
using v8::Maybe;
43
using v8::MaybeLocal;
44
using v8::NewStringType;
45
using v8::Null;
46
using v8::Number;
47
using v8::Object;
48
using v8::ObjectTemplate;
49
using v8::ResourceConstraints;
50
using v8::SealHandleScope;
51
using v8::String;
52
using v8::TryCatch;
53
using v8::Value;
54
55
namespace node {
56
namespace worker {
57
58
constexpr double kMB = 1024 * 1024;
59
60
Worker::Worker(Environment* env,
61
               Local<Object> wrap,
62
               const std::string& url,
63
               const std::string& name,
64
               std::shared_ptr<PerIsolateOptions> per_isolate_opts,
65
               std::vector<std::string>&& exec_argv,
66
               std::shared_ptr<KVStore> env_vars,
67
               const SnapshotData* snapshot_data,
68
               const bool is_internal)
69
0
    : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_WORKER),
70
0
      per_isolate_opts_(per_isolate_opts),
71
0
      exec_argv_(exec_argv),
72
0
      platform_(env->isolate_data()->platform()),
73
0
      thread_id_(AllocateEnvironmentThreadId()),
74
0
      name_(name),
75
0
      env_vars_(env_vars),
76
0
      embedder_preload_(env->embedder_preload()),
77
0
      snapshot_data_(snapshot_data),
78
0
      is_internal_(is_internal) {
79
0
  Debug(this, "Creating new worker instance with thread id %llu",
80
0
        thread_id_.id);
81
82
  // Set up everything that needs to be set up in the parent environment.
83
0
  MessagePort* parent_port = MessagePort::New(env, env->context());
84
0
  if (parent_port == nullptr) {
85
    // This can happen e.g. because execution is terminating.
86
0
    return;
87
0
  }
88
89
0
  child_port_data_ = std::make_unique<MessagePortData>(nullptr);
90
0
  MessagePort::Entangle(parent_port, child_port_data_.get());
91
92
0
  object()
93
0
      ->Set(env->context(), env->message_port_string(), parent_port->object())
94
0
      .Check();
95
96
0
  object()->Set(env->context(),
97
0
                env->thread_id_string(),
98
0
                Number::New(env->isolate(), static_cast<double>(thread_id_.id)))
99
0
      .Check();
100
101
0
  object()
102
0
      ->Set(env->context(),
103
0
            env->thread_name_string(),
104
0
            String::NewFromUtf8(env->isolate(),
105
0
                                name_.data(),
106
0
                                NewStringType::kNormal,
107
0
                                name_.size())
108
0
                .ToLocalChecked())
109
0
      .Check();
110
  // Without this check, to use the permission model with
111
  // workers (--allow-worker) one would need to pass --allow-inspector as well
112
0
  if (env->permission()->is_granted(
113
0
          env, node::permission::PermissionScope::kInspector)) {
114
0
    inspector_parent_handle_ =
115
0
        GetInspectorParentHandle(env, thread_id_, url, name);
116
0
  }
117
118
0
  argv_ = std::vector<std::string>{env->argv()[0]};
119
  // Mark this Worker object as weak until we actually start the thread.
120
0
  MakeWeak();
121
122
0
  Debug(this, "Preparation for worker %llu finished", thread_id_.id);
123
0
}
124
125
0
bool Worker::is_stopped() const {
126
0
  Mutex::ScopedLock lock(mutex_);
127
0
  if (env_ != nullptr)
128
0
    return env_->is_stopping();
129
0
  return stopped_;
130
0
}
131
132
0
void Worker::UpdateResourceConstraints(ResourceConstraints* constraints) {
133
0
  constraints->set_stack_limit(reinterpret_cast<uint32_t*>(stack_base_));
134
135
0
  if (resource_limits_[kMaxYoungGenerationSizeMb] > 0) {
136
0
    constraints->set_max_young_generation_size_in_bytes(
137
0
        static_cast<size_t>(resource_limits_[kMaxYoungGenerationSizeMb] * kMB));
138
0
  } else {
139
0
    resource_limits_[kMaxYoungGenerationSizeMb] =
140
0
        constraints->max_young_generation_size_in_bytes() / kMB;
141
0
  }
142
143
0
  if (resource_limits_[kMaxOldGenerationSizeMb] > 0) {
144
0
    constraints->set_max_old_generation_size_in_bytes(
145
0
        static_cast<size_t>(resource_limits_[kMaxOldGenerationSizeMb] * kMB));
146
0
  } else {
147
0
    resource_limits_[kMaxOldGenerationSizeMb] =
148
0
        constraints->max_old_generation_size_in_bytes() / kMB;
149
0
  }
150
151
0
  if (resource_limits_[kCodeRangeSizeMb] > 0) {
152
0
    constraints->set_code_range_size_in_bytes(
153
0
        static_cast<size_t>(resource_limits_[kCodeRangeSizeMb] * kMB));
154
0
  } else {
155
0
    resource_limits_[kCodeRangeSizeMb] =
156
0
        constraints->code_range_size_in_bytes() / kMB;
157
0
  }
158
0
}
159
160
// This class contains data that is only relevant to the child thread itself,
161
// and only while it is running.
162
// (Eventually, the Environment instance should probably also be moved here.)
163
class WorkerThreadData {
164
 public:
165
  explicit WorkerThreadData(Worker* w)
166
0
    : w_(w) {
167
0
    int ret = uv_loop_init(&loop_);
168
0
    if (ret != 0) {
169
0
      char err_buf[128];
170
0
      uv_err_name_r(ret, err_buf, sizeof(err_buf));
171
      // TODO(joyeecheung): maybe this should be kBootstrapFailure instead?
172
0
      w->Exit(ExitCode::kGenericUserError, "ERR_WORKER_INIT_FAILED", err_buf);
173
0
      return;
174
0
    }
175
0
    loop_init_failed_ = false;
176
0
    uv_loop_configure(&loop_, UV_METRICS_IDLE_TIME);
177
178
0
    std::shared_ptr<ArrayBufferAllocator> allocator =
179
0
        ArrayBufferAllocator::Create();
180
0
    Isolate::CreateParams params;
181
0
    SetIsolateCreateParamsForNode(&params);
182
0
    w->UpdateResourceConstraints(&params.constraints);
183
0
    params.array_buffer_allocator_shared = allocator;
184
0
    Isolate* isolate =
185
0
        NewIsolate(&params, &loop_, w->platform_, w->snapshot_data());
186
0
    if (isolate == nullptr) {
187
      // TODO(joyeecheung): maybe this should be kBootstrapFailure instead?
188
0
      w->Exit(ExitCode::kGenericUserError,
189
0
              "ERR_WORKER_INIT_FAILED",
190
0
              "Failed to create new Isolate");
191
0
      return;
192
0
    }
193
194
0
    SetIsolateUpForNode(isolate);
195
196
    // Be sure it's called before Environment::InitializeDiagnostics()
197
    // so that this callback stays when the callback of
198
    // --heapsnapshot-near-heap-limit gets is popped.
199
0
    isolate->AddNearHeapLimitCallback(Worker::NearHeapLimit, w);
200
201
0
    {
202
0
      Locker locker(isolate);
203
0
      Isolate::Scope isolate_scope(isolate);
204
      // V8 computes its stack limit the first time a `Locker` is used based on
205
      // --stack-size. Reset it to the correct value.
206
0
      isolate->SetStackLimit(w->stack_base_);
207
208
0
      HandleScope handle_scope(isolate);
209
0
      isolate_data_.reset(IsolateData::CreateIsolateData(
210
0
          isolate,
211
0
          &loop_,
212
0
          w_->platform_,
213
0
          allocator.get(),
214
0
          w->snapshot_data()->AsEmbedderWrapper().get(),
215
0
          std::move(w_->per_isolate_opts_)));
216
0
      CHECK(isolate_data_);
217
0
      CHECK(!isolate_data_->is_building_snapshot());
218
0
      isolate_data_->set_worker_context(w_);
219
0
      isolate_data_->max_young_gen_size =
220
0
          params.constraints.max_young_generation_size_in_bytes();
221
0
    }
222
223
0
    Mutex::ScopedLock lock(w_->mutex_);
224
0
    w_->isolate_ = isolate;
225
0
  }
226
227
0
  ~WorkerThreadData() {
228
0
    Debug(w_, "Worker %llu dispose isolate", w_->thread_id_.id);
229
0
    Isolate* isolate;
230
0
    {
231
0
      Mutex::ScopedLock lock(w_->mutex_);
232
0
      isolate = w_->isolate_;
233
0
      w_->isolate_ = nullptr;
234
0
    }
235
236
0
    if (isolate != nullptr) {
237
0
      CHECK(!loop_init_failed_);
238
0
      bool platform_finished = false;
239
240
      // https://github.com/nodejs/node/issues/51129 - IsolateData destructor
241
      // can kick off GC before teardown, so ensure the isolate is entered.
242
0
      {
243
0
        Locker locker(isolate);
244
0
        Isolate::Scope isolate_scope(isolate);
245
0
        isolate_data_.reset();
246
0
      }
247
248
0
      w_->platform_->AddIsolateFinishedCallback(isolate, [](void* data) {
249
0
        *static_cast<bool*>(data) = true;
250
0
      }, &platform_finished);
251
252
0
      w_->platform_->DisposeIsolate(isolate);
253
254
      // Wait until the platform has cleaned up all relevant resources.
255
0
      while (!platform_finished) {
256
0
        uv_run(&loop_, UV_RUN_ONCE);
257
0
      }
258
0
    }
259
0
    if (!loop_init_failed_) {
260
0
      CheckedUvLoopClose(&loop_);
261
0
    }
262
0
  }
263
264
0
  bool loop_is_usable() const { return !loop_init_failed_; }
265
266
 private:
267
  Worker* const w_;
268
  uv_loop_t loop_;
269
  bool loop_init_failed_ = true;
270
  DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data_;
271
  friend class Worker;
272
};
273
274
size_t Worker::NearHeapLimit(void* data, size_t current_heap_limit,
275
0
                             size_t initial_heap_limit) {
276
0
  Worker* worker = static_cast<Worker*>(data);
277
  // Give the current GC some extra leeway to let it finish rather than
278
  // crash hard. We are not going to perform further allocations anyway.
279
0
  constexpr size_t kExtraHeapAllowance = 16 * 1024 * 1024;
280
0
  size_t new_limit = current_heap_limit + kExtraHeapAllowance;
281
0
  Environment* env = worker->env();
282
0
  if (env != nullptr) {
283
0
    DCHECK(!env->is_in_heapsnapshot_heap_limit_callback());
284
0
    Debug(env,
285
0
          DebugCategory::DIAGNOSTICS,
286
0
          "Throwing ERR_WORKER_OUT_OF_MEMORY, "
287
0
          "new_limit=%" PRIu64 "\n",
288
0
          static_cast<uint64_t>(new_limit));
289
0
  }
290
  // TODO(joyeecheung): maybe this should be kV8FatalError instead?
291
0
  worker->Exit(ExitCode::kGenericUserError,
292
0
               "ERR_WORKER_OUT_OF_MEMORY",
293
0
               "JS heap out of memory");
294
0
  return new_limit;
295
0
}
296
297
0
void Worker::Run() {
298
0
  std::string trace_name = "[worker " + std::to_string(thread_id_.id) + "]" +
299
0
                           (name_ == "" ? "" : " " + name_);
300
0
  TRACE_EVENT_METADATA1(
301
0
      "__metadata", "thread_name", "name", TRACE_STR_COPY(trace_name.c_str()));
302
0
  CHECK_NOT_NULL(platform_);
303
304
0
  Debug(this, "Creating isolate for worker with id %llu", thread_id_.id);
305
306
0
  WorkerThreadData data(this);
307
0
  if (isolate_ == nullptr) return;
308
0
  CHECK(data.loop_is_usable());
309
310
0
  Debug(this, "Starting worker with id %llu", thread_id_.id);
311
0
  {
312
0
    Locker locker(isolate_);
313
0
    Isolate::Scope isolate_scope(isolate_);
314
0
    SealHandleScope outer_seal(isolate_);
315
316
0
    DeleteFnPtr<Environment, FreeEnvironment> env_;
317
0
    auto cleanup_env = OnScopeLeave([&]() {
318
      // TODO(addaleax): This call is harmless but should not be necessary.
319
      // Figure out why V8 is raising a DCHECK() here without it
320
      // (in test/parallel/test-async-hooks-worker-asyncfn-terminate-4.js).
321
0
      isolate_->CancelTerminateExecution();
322
323
0
      if (!env_) return;
324
0
      env_->set_can_call_into_js(false);
325
326
0
      {
327
0
        Mutex::ScopedLock lock(mutex_);
328
0
        stopped_ = true;
329
0
        this->env_ = nullptr;
330
0
      }
331
332
0
      env_.reset();
333
0
    });
334
335
0
    if (is_stopped()) return;
336
0
    {
337
0
      HandleScope handle_scope(isolate_);
338
0
      Local<Context> context;
339
0
      {
340
        // We create the Context object before we have an Environment* in place
341
        // that we could use for error handling. If creation fails due to
342
        // resource constraints, we need something in place to handle it,
343
        // though.
344
0
        TryCatch try_catch(isolate_);
345
0
        if (snapshot_data_ != nullptr) {
346
0
          Debug(this,
347
0
                "Worker %llu uses context from snapshot %d\n",
348
0
                thread_id_.id,
349
0
                static_cast<int>(SnapshotData::kNodeBaseContextIndex));
350
0
          context = Context::FromSnapshot(isolate_,
351
0
                                          SnapshotData::kNodeBaseContextIndex)
352
0
                        .ToLocalChecked();
353
0
          if (!context.IsEmpty() &&
354
0
              !InitializeContextRuntime(context).IsJust()) {
355
0
            context = Local<Context>();
356
0
          }
357
0
        } else {
358
0
          Debug(
359
0
              this, "Worker %llu builds context from scratch\n", thread_id_.id);
360
0
          context = NewContext(isolate_);
361
0
        }
362
0
        if (context.IsEmpty()) {
363
          // TODO(joyeecheung): maybe this should be kBootstrapFailure instead?
364
0
          Exit(ExitCode::kGenericUserError,
365
0
               "ERR_WORKER_INIT_FAILED",
366
0
               "Failed to create new Context");
367
0
          return;
368
0
        }
369
0
      }
370
371
0
      if (is_stopped()) return;
372
0
      CHECK(!context.IsEmpty());
373
0
      Context::Scope context_scope(context);
374
0
      {
375
0
#if HAVE_INSPECTOR
376
0
        environment_flags_ |= EnvironmentFlags::kNoWaitForInspectorFrontend;
377
0
#endif
378
0
        env_.reset(CreateEnvironment(
379
0
            data.isolate_data_.get(),
380
0
            context,
381
0
            std::move(argv_),
382
0
            std::move(exec_argv_),
383
0
            static_cast<EnvironmentFlags::Flags>(environment_flags_),
384
0
            thread_id_,
385
0
            std::move(inspector_parent_handle_),
386
0
            name_));
387
0
        if (is_stopped()) return;
388
0
        CHECK_NOT_NULL(env_);
389
0
        env_->set_env_vars(std::move(env_vars_));
390
0
        SetProcessExitHandler(env_.get(), [this](Environment*, int exit_code) {
391
0
          Exit(static_cast<ExitCode>(exit_code));
392
0
        });
393
0
      }
394
0
      {
395
0
        Mutex::ScopedLock lock(mutex_);
396
0
        if (stopped_) return;
397
0
        this->env_ = env_.get();
398
0
      }
399
0
      Debug(this, "Created Environment for worker with id %llu", thread_id_.id);
400
401
0
#if HAVE_INSPECTOR
402
0
      this->env_->WaitForInspectorFrontendByOptions();
403
0
#endif
404
0
      if (is_stopped()) return;
405
0
      {
406
0
        if (!CreateEnvMessagePort(env_.get())) {
407
0
          return;
408
0
        }
409
410
0
        Debug(this, "Created message port for worker %llu", thread_id_.id);
411
0
        if (LoadEnvironment(env_.get(),
412
0
                            StartExecutionCallback{},
413
0
                            std::move(embedder_preload_))
414
0
                .IsEmpty()) {
415
0
          return;
416
0
        }
417
418
0
        Debug(this, "Loaded environment for worker %llu", thread_id_.id);
419
0
      }
420
0
    }
421
422
0
    {
423
0
      Maybe<ExitCode> exit_code = SpinEventLoopInternal(env_.get());
424
0
      Mutex::ScopedLock lock(mutex_);
425
0
      if (exit_code_ == ExitCode::kNoFailure && exit_code.IsJust()) {
426
0
        exit_code_ = exit_code.FromJust();
427
0
      }
428
429
0
      Debug(this,
430
0
            "Exiting thread for worker %llu with exit code %d",
431
0
            thread_id_.id,
432
0
            static_cast<int>(exit_code_));
433
0
    }
434
0
  }
435
436
0
  Debug(this, "Worker %llu thread stops", thread_id_.id);
437
0
}
438
439
0
bool Worker::CreateEnvMessagePort(Environment* env) {
440
0
  HandleScope handle_scope(isolate_);
441
0
  std::unique_ptr<MessagePortData> data;
442
0
  {
443
0
    Mutex::ScopedLock lock(mutex_);
444
0
    data = std::move(child_port_data_);
445
0
  }
446
447
  // Set up the message channel for receiving messages in the child.
448
0
  MessagePort* child_port = MessagePort::New(env,
449
0
                                             env->context(),
450
0
                                             std::move(data));
451
  // MessagePort::New() may return nullptr if execution is terminated
452
  // within it.
453
0
  if (child_port != nullptr)
454
0
    env->set_message_port(child_port->object(isolate_));
455
456
0
  return child_port;
457
0
}
458
459
0
void Worker::JoinThread() {
460
0
  if (!tid_.has_value())
461
0
    return;
462
0
  CHECK_EQ(uv_thread_join(&tid_.value()), 0);
463
0
  tid_.reset();
464
465
0
  env()->remove_sub_worker_context(this);
466
467
  // Join may happen after the worker exits and disposes the isolate
468
0
  if (!env()->can_call_into_js()) return;
469
470
0
  {
471
0
    HandleScope handle_scope(env()->isolate());
472
0
    Context::Scope context_scope(env()->context());
473
474
    // Reset the parent port as we're closing it now anyway.
475
0
    object()->Set(env()->context(),
476
0
                  env()->message_port_string(),
477
0
                  Undefined(env()->isolate())).Check();
478
479
0
    Local<Value> args[] = {
480
0
        Integer::New(env()->isolate(), static_cast<int>(exit_code_)),
481
0
        custom_error_ != nullptr
482
0
            ? OneByteString(env()->isolate(), custom_error_).As<Value>()
483
0
            : Null(env()->isolate()).As<Value>(),
484
0
        !custom_error_str_.empty()
485
0
            ? OneByteString(env()->isolate(), custom_error_str_.c_str())
486
0
                  .As<Value>()
487
0
            : Null(env()->isolate()).As<Value>(),
488
0
    };
489
490
0
    MakeCallback(env()->onexit_string(), arraysize(args), args);
491
0
  }
492
493
  // If we get here, the tid_.has_value() condition at the top of the function
494
  // implies that the thread was running. In that case, its final action will
495
  // be to schedule a callback on the parent thread which will delete this
496
  // object, so there's nothing more to do here.
497
0
}
498
499
0
Worker::~Worker() {
500
0
  Mutex::ScopedLock lock(mutex_);
501
502
0
  CHECK(stopped_);
503
0
  CHECK_NULL(env_);
504
0
  CHECK(!tid_.has_value());
505
0
  Debug(this, "Worker %llu destroyed", thread_id_.id);
506
0
}
507
508
0
void Worker::New(const FunctionCallbackInfo<Value>& args) {
509
0
  Environment* env = Environment::GetCurrent(args);
510
0
  THROW_IF_INSUFFICIENT_PERMISSIONS(
511
0
      env, permission::PermissionScope::kWorkerThreads, "");
512
0
  bool is_internal = args[5]->IsTrue();
513
0
  Isolate* isolate = args.GetIsolate();
514
515
0
  CHECK(args.IsConstructCall());
516
517
0
  if (env->isolate_data()->platform() == nullptr) {
518
0
    THROW_ERR_MISSING_PLATFORM_FOR_WORKER(env);
519
0
    return;
520
0
  }
521
0
  CHECK(!env->isolate_data()->is_building_snapshot());
522
523
0
  std::string url;
524
0
  std::string name;
525
0
  std::shared_ptr<PerIsolateOptions> per_isolate_opts = nullptr;
526
0
  std::shared_ptr<KVStore> env_vars = nullptr;
527
528
0
  std::vector<std::string> exec_argv_out;
529
530
  // Argument might be a string or URL
531
0
  if (!args[0]->IsNullOrUndefined()) {
532
0
    Utf8Value value(
533
0
        isolate, args[0]->ToString(env->context()).FromMaybe(Local<String>()));
534
0
    url.append(value.out(), value.length());
535
0
  }
536
537
0
  if (!args[6]->IsNullOrUndefined()) {
538
0
    Utf8Value value(
539
0
        isolate, args[6]->ToString(env->context()).FromMaybe(Local<String>()));
540
0
    name.append(value.out(), value.length());
541
0
  }
542
543
0
  if (args[1]->IsNull()) {
544
    // Means worker.env = { ...process.env }.
545
0
    env_vars = env->env_vars()->Clone(isolate);
546
0
  } else if (args[1]->IsObject()) {
547
    // User provided env.
548
0
    env_vars = KVStore::CreateMapKVStore();
549
0
    if (env_vars
550
0
            ->AssignFromObject(isolate->GetCurrentContext(),
551
0
                               args[1].As<Object>())
552
0
            .IsNothing()) {
553
0
      return;
554
0
    }
555
0
  } else {
556
    // Env is shared.
557
0
    env_vars = env->env_vars();
558
0
  }
559
560
0
  if (!env_vars) {
561
0
    THROW_ERR_OPERATION_FAILED(env, "Failed to copy environment variables");
562
0
  }
563
564
0
  if (args[1]->IsObject() || args[2]->IsArray()) {
565
0
    per_isolate_opts.reset(new PerIsolateOptions());
566
567
0
    HandleEnvOptions(per_isolate_opts->per_env, [&env_vars](const char* name) {
568
0
      return env_vars->Get(name).value_or("");
569
0
    });
570
571
0
#ifndef NODE_WITHOUT_NODE_OPTIONS
572
0
    std::optional<std::string> node_options = env_vars->Get("NODE_OPTIONS");
573
0
    if (node_options.has_value()) {
574
0
      std::vector<std::string> errors{};
575
0
      std::vector<std::string> env_argv =
576
0
          ParseNodeOptionsEnvVar(node_options.value(), &errors);
577
      // [0] is expected to be the program name, add dummy string.
578
0
      env_argv.insert(env_argv.begin(), "");
579
0
      std::vector<std::string> invalid_args{};
580
581
0
      std::optional<std::string> parent_node_options =
582
0
          env->env_vars()->Get("NODE_OPTIONS");
583
584
      // If the worker code passes { env: { ...process.env, ... } } or
585
      // the NODE_OPTIONS is otherwise character-for-character equal to the
586
      // original NODE_OPTIONS, allow per-process options inherited into
587
      // the worker since worker spawning code is not usually in charge of
588
      // how the NODE_OPTIONS is configured for the parent.
589
      // TODO(joyeecheung): a more intelligent filter may be more desirable.
590
      // but a string comparison is good enough(TM) for the case where the
591
      // worker spawning code just wants to pass the parent configuration down
592
      // and does not intend to modify NODE_OPTIONS.
593
0
      if (parent_node_options == node_options) {
594
        // Creates a wrapper per-process option over the per_isolate_opts
595
        // to allow per-process options copied from the parent.
596
0
        std::unique_ptr<PerProcessOptions> per_process_opts =
597
0
            std::make_unique<PerProcessOptions>();
598
0
        per_process_opts->per_isolate = per_isolate_opts;
599
0
        options_parser::Parse(&env_argv,
600
0
                              nullptr,
601
0
                              &invalid_args,
602
0
                              per_process_opts.get(),
603
0
                              kAllowedInEnvvar,
604
0
                              &errors);
605
0
      } else {
606
0
        options_parser::Parse(&env_argv,
607
0
                              nullptr,
608
0
                              &invalid_args,
609
0
                              per_isolate_opts.get(),
610
0
                              kAllowedInEnvvar,
611
0
                              &errors);
612
0
      }
613
614
0
      if (!errors.empty() && args[1]->IsObject()) {
615
        // Only fail for explicitly provided env, this protects from failures
616
        // when NODE_OPTIONS from parent's env is used (which is the default).
617
0
        Local<Value> error;
618
0
        if (!ToV8Value(env->context(), errors).ToLocal(&error)) return;
619
0
        Local<String> key =
620
0
            FIXED_ONE_BYTE_STRING(env->isolate(), "invalidNodeOptions");
621
        // Ignore the return value of Set() because exceptions bubble up to JS
622
        // when we return anyway.
623
0
        USE(args.This()->Set(env->context(), key, error));
624
0
        return;
625
0
      }
626
0
    }
627
0
#endif  // NODE_WITHOUT_NODE_OPTIONS
628
629
    // The first argument is reserved for program name, but we don't need it
630
    // in workers.
631
0
    std::vector<std::string> exec_argv = {""};
632
0
    if (args[2]->IsArray()) {
633
0
      Local<Array> array = args[2].As<Array>();
634
0
      uint32_t length = array->Length();
635
0
      for (uint32_t i = 0; i < length; i++) {
636
0
        Local<Value> arg;
637
0
        if (!array->Get(env->context(), i).ToLocal(&arg)) {
638
0
          return;
639
0
        }
640
0
        Local<String> arg_v8;
641
0
        if (!arg->ToString(env->context()).ToLocal(&arg_v8)) {
642
0
          return;
643
0
        }
644
0
        Utf8Value arg_utf8_value(args.GetIsolate(), arg_v8);
645
0
        std::string arg_string(arg_utf8_value.out(), arg_utf8_value.length());
646
0
        exec_argv.push_back(arg_string);
647
0
      }
648
0
    } else {
649
0
      exec_argv.insert(
650
0
          exec_argv.end(), env->exec_argv().begin(), env->exec_argv().end());
651
0
    }
652
653
0
    std::vector<std::string> invalid_args{};
654
0
    std::vector<std::string> errors{};
655
    // Using invalid_args as the v8_args argument as it stores unknown
656
    // options for the per isolate parser.
657
0
    options_parser::Parse(&exec_argv,
658
0
                          &exec_argv_out,
659
0
                          &invalid_args,
660
0
                          per_isolate_opts.get(),
661
0
                          kDisallowedInEnvvar,
662
0
                          &errors);
663
664
    // The first argument is program name.
665
0
    invalid_args.erase(invalid_args.begin());
666
    // Only fail for explicitly provided execArgv, this protects from failures
667
    // when execArgv from parent's execArgv is used (which is the default).
668
0
    if (errors.size() > 0 || (invalid_args.size() > 0 && args[2]->IsArray())) {
669
0
      Local<Value> error;
670
0
      if (!ToV8Value(env->context(), errors.size() > 0 ? errors : invalid_args)
671
0
               .ToLocal(&error)) {
672
0
        return;
673
0
      }
674
0
      Local<String> key =
675
0
          FIXED_ONE_BYTE_STRING(env->isolate(), "invalidExecArgv");
676
      // Ignore the return value of Set() because exceptions bubble up to JS
677
      // when we return anyway.
678
0
      USE(args.This()->Set(env->context(), key, error));
679
0
      return;
680
0
    }
681
0
  } else {
682
    // Copy the parent's execArgv.
683
0
    exec_argv_out = env->exec_argv();
684
0
    per_isolate_opts = env->isolate_data()->options()->Clone();
685
0
  }
686
687
  // Internal workers should not wait for inspector frontend to connect or
688
  // break on the first line of internal scripts. Module loader threads are
689
  // essential to load user codes and must not be blocked by the inspector
690
  // for internal scripts.
691
  // Still, `--inspect-node` can break on the first line of internal scripts.
692
0
  if (is_internal) {
693
0
    per_isolate_opts->per_env->get_debug_options()
694
0
        ->DisableWaitOrBreakFirstLine();
695
0
  }
696
697
0
  const SnapshotData* snapshot_data = env->isolate_data()->snapshot_data();
698
699
0
  Worker* worker = new Worker(env,
700
0
                              args.This(),
701
0
                              url,
702
0
                              name,
703
0
                              per_isolate_opts,
704
0
                              std::move(exec_argv_out),
705
0
                              env_vars,
706
0
                              snapshot_data,
707
0
                              is_internal);
708
709
0
  CHECK(args[3]->IsFloat64Array());
710
0
  Local<Float64Array> limit_info = args[3].As<Float64Array>();
711
0
  CHECK_EQ(limit_info->Length(), kTotalResourceLimitCount);
712
0
  limit_info->CopyContents(worker->resource_limits_,
713
0
                           sizeof(worker->resource_limits_));
714
715
0
  CHECK(args[4]->IsBoolean());
716
0
  if (args[4]->IsTrue() || env->tracks_unmanaged_fds())
717
0
    worker->environment_flags_ |= EnvironmentFlags::kTrackUnmanagedFds;
718
0
  if (env->hide_console_windows())
719
0
    worker->environment_flags_ |= EnvironmentFlags::kHideConsoleWindows;
720
0
  if (env->no_native_addons())
721
0
    worker->environment_flags_ |= EnvironmentFlags::kNoNativeAddons;
722
0
  if (env->no_global_search_paths())
723
0
    worker->environment_flags_ |= EnvironmentFlags::kNoGlobalSearchPaths;
724
0
  if (env->no_browser_globals())
725
0
    worker->environment_flags_ |= EnvironmentFlags::kNoBrowserGlobals;
726
0
}
727
728
0
void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {
729
0
  Worker* w;
730
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
731
0
  Mutex::ScopedLock lock(w->mutex_);
732
733
0
  w->stopped_ = false;
734
735
0
  if (w->resource_limits_[kStackSizeMb] > 0) {
736
0
    if (w->resource_limits_[kStackSizeMb] * kMB < kStackBufferSize) {
737
0
      w->resource_limits_[kStackSizeMb] = kStackBufferSize / kMB;
738
0
      w->stack_size_ = kStackBufferSize;
739
0
    } else {
740
0
      w->stack_size_ =
741
0
          static_cast<size_t>(w->resource_limits_[kStackSizeMb] * kMB);
742
0
    }
743
0
  } else {
744
0
    w->resource_limits_[kStackSizeMb] = w->stack_size_ / kMB;
745
0
  }
746
747
0
  uv_thread_options_t thread_options;
748
0
  thread_options.flags = UV_THREAD_HAS_STACK_SIZE;
749
0
  thread_options.stack_size = w->stack_size_;
750
751
0
  uv_thread_t* tid = &w->tid_.emplace();  // Create uv_thread_t instance
752
0
  int ret = uv_thread_create_ex(tid, &thread_options, [](void* arg) {
753
    // XXX: This could become a std::unique_ptr, but that makes at least
754
    // gcc 6.3 detect undefined behaviour when there shouldn't be any.
755
    // gcc 7+ handles this well.
756
0
    Worker* w = static_cast<Worker*>(arg);
757
0
    const uintptr_t stack_top = reinterpret_cast<uintptr_t>(&arg);
758
759
0
    uv_thread_setname(w->name_.c_str());
760
    // Leave a few kilobytes just to make sure we're within limits and have
761
    // some space to do work in C++ land.
762
0
    w->stack_base_ = stack_top - (w->stack_size_ - kStackBufferSize);
763
764
0
    w->Run();
765
766
0
    Mutex::ScopedLock lock(w->mutex_);
767
0
    w->env()->SetImmediateThreadsafe(
768
0
        [w = std::unique_ptr<Worker>(w)](Environment* env) {
769
0
          if (w->has_ref_)
770
0
            env->add_refs(-1);
771
0
          w->JoinThread();
772
          // implicitly delete w
773
0
        });
774
0
  }, static_cast<void*>(w));
775
776
0
  if (ret == 0) {
777
    // The object now owns the created thread and should not be garbage
778
    // collected until that finishes.
779
0
    w->ClearWeak();
780
781
0
    if (w->has_ref_)
782
0
      w->env()->add_refs(1);
783
784
0
    w->env()->add_sub_worker_context(w);
785
0
  } else {
786
0
    w->stopped_ = true;
787
0
    w->tid_.reset();
788
789
0
    char err_buf[128];
790
0
    uv_err_name_r(ret, err_buf, sizeof(err_buf));
791
0
    {
792
0
      Isolate* isolate = w->env()->isolate();
793
0
      HandleScope handle_scope(isolate);
794
0
      THROW_ERR_WORKER_INIT_FAILED(isolate, err_buf);
795
0
    }
796
0
  }
797
0
}
798
799
0
void Worker::StopThread(const FunctionCallbackInfo<Value>& args) {
800
0
  Worker* w;
801
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
802
803
0
  Debug(w, "Worker %llu is getting stopped by parent", w->thread_id_.id);
804
0
  w->Exit(ExitCode::kGenericUserError);
805
0
}
806
807
0
void Worker::Ref(const FunctionCallbackInfo<Value>& args) {
808
0
  Worker* w;
809
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
810
0
  if (!w->has_ref_ && w->tid_.has_value()) {
811
0
    w->has_ref_ = true;
812
0
    w->env()->add_refs(1);
813
0
  }
814
0
}
815
816
0
void Worker::HasRef(const FunctionCallbackInfo<Value>& args) {
817
0
  Worker* w;
818
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
819
0
  args.GetReturnValue().Set(w->has_ref_);
820
0
}
821
822
0
void Worker::Unref(const FunctionCallbackInfo<Value>& args) {
823
0
  Worker* w;
824
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
825
0
  if (w->has_ref_ && w->tid_.has_value()) {
826
0
    w->has_ref_ = false;
827
0
    w->env()->add_refs(-1);
828
0
  }
829
0
}
830
831
class WorkerCpuUsageTaker : public AsyncWrap {
832
 public:
833
  WorkerCpuUsageTaker(Environment* env, Local<Object> obj)
834
0
      : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERCPUUSAGE) {}
835
836
  SET_NO_MEMORY_INFO()
837
  SET_MEMORY_INFO_NAME(WorkerCpuUsageTaker)
838
  SET_SELF_SIZE(WorkerCpuUsageTaker)
839
};
840
841
0
void Worker::CpuUsage(const FunctionCallbackInfo<Value>& args) {
842
0
  Worker* w;
843
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
844
845
0
  Environment* env = w->env();
846
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
847
0
  Local<Object> wrap;
848
0
  if (!env->worker_cpu_usage_taker_template()
849
0
           ->NewInstance(env->context())
850
0
           .ToLocal(&wrap)) {
851
0
    return;
852
0
  }
853
854
0
  BaseObjectPtr<WorkerCpuUsageTaker> taker =
855
0
      MakeDetachedBaseObject<WorkerCpuUsageTaker>(env, wrap);
856
857
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker),
858
0
                                        env](Environment* worker_env) mutable {
859
0
    auto cpu_usage_stats = std::make_unique<uv_rusage_t>();
860
0
    int err = uv_getrusage_thread(cpu_usage_stats.get());
861
862
0
    env->SetImmediateThreadsafe(
863
0
        [taker = std::move(taker),
864
0
         cpu_usage_stats = std::move(cpu_usage_stats),
865
0
         err = err](Environment* env) mutable {
866
0
          Isolate* isolate = env->isolate();
867
0
          HandleScope handle_scope(isolate);
868
0
          Context::Scope context_scope(env->context());
869
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
870
871
0
          Local<Value> argv[] = {
872
0
              Null(isolate),
873
0
              Undefined(isolate),
874
0
          };
875
876
0
          if (err) {
877
0
            argv[0] = UVException(
878
0
                isolate, err, "uv_getrusage_thread", nullptr, nullptr, nullptr);
879
0
          } else {
880
0
            auto tmpl = env->cpu_usage_template();
881
0
            if (tmpl.IsEmpty()) {
882
0
              static constexpr std::string_view names[] = {
883
0
                  "user",
884
0
                  "system",
885
0
              };
886
0
              tmpl = DictionaryTemplate::New(isolate, names);
887
0
              env->set_cpu_usage_template(tmpl);
888
0
            }
889
890
0
            MaybeLocal<Value> values[] = {
891
0
                Number::New(isolate,
892
0
                            1e6 * cpu_usage_stats->ru_utime.tv_sec +
893
0
                                cpu_usage_stats->ru_utime.tv_usec),
894
0
                Number::New(isolate,
895
0
                            1e6 * cpu_usage_stats->ru_stime.tv_sec +
896
0
                                cpu_usage_stats->ru_stime.tv_usec),
897
0
            };
898
0
            if (!NewDictionaryInstanceNullProto(env->context(), tmpl, values)
899
0
                     .ToLocal(&argv[1])) {
900
0
              return;
901
0
            }
902
0
          }
903
904
0
          taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
905
0
        },
906
0
        CallbackFlags::kUnrefed);
907
0
  });
908
909
0
  if (scheduled) {
910
0
    args.GetReturnValue().Set(wrap);
911
0
  }
912
0
}
913
914
class WorkerCpuProfileTaker final : public AsyncWrap {
915
 public:
916
  WorkerCpuProfileTaker(Environment* env, Local<Object> obj)
917
0
      : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERCPUPROFILE) {}
918
919
  SET_NO_MEMORY_INFO()
920
  SET_MEMORY_INFO_NAME(WorkerCpuProfileTaker)
921
  SET_SELF_SIZE(WorkerCpuProfileTaker)
922
};
923
924
0
void Worker::StartCpuProfile(const FunctionCallbackInfo<Value>& args) {
925
0
  Worker* w;
926
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
927
0
  Environment* env = w->env();
928
929
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
930
0
  Local<Object> wrap;
931
0
  if (!env->worker_cpu_profile_taker_template()
932
0
           ->NewInstance(env->context())
933
0
           .ToLocal(&wrap)) {
934
0
    return;
935
0
  }
936
937
0
  BaseObjectPtr<WorkerCpuProfileTaker> taker =
938
0
      MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap);
939
940
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker),
941
0
                                        env](Environment* worker_env) mutable {
942
0
    CpuProfilingResult result = worker_env->StartCpuProfile();
943
0
    env->SetImmediateThreadsafe(
944
0
        [taker = std::move(taker), result = result](Environment* env) mutable {
945
0
          Isolate* isolate = env->isolate();
946
0
          HandleScope handle_scope(isolate);
947
0
          Context::Scope context_scope(env->context());
948
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
949
0
          Local<Value> argv[] = {
950
0
              Null(isolate),       // error
951
0
              Undefined(isolate),  // profile id
952
0
          };
953
0
          if (result.status == CpuProfilingStatus::kErrorTooManyProfilers) {
954
0
            argv[0] = ERR_CPU_PROFILE_TOO_MANY(
955
0
                isolate, "There are too many CPU profiles");
956
0
          } else if (result.status == CpuProfilingStatus::kStarted) {
957
0
            argv[1] = Number::New(isolate, result.id);
958
0
          }
959
0
          taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
960
0
        },
961
0
        CallbackFlags::kUnrefed);
962
0
  });
963
964
0
  if (scheduled) {
965
0
    args.GetReturnValue().Set(wrap);
966
0
  }
967
0
}
968
969
0
void Worker::StopCpuProfile(const FunctionCallbackInfo<Value>& args) {
970
0
  Worker* w;
971
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
972
973
0
  Environment* env = w->env();
974
0
  CHECK(args[0]->IsUint32());
975
0
  uint32_t profile_id = args[0]->Uint32Value(env->context()).FromJust();
976
977
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
978
0
  Local<Object> wrap;
979
0
  if (!env->worker_cpu_profile_taker_template()
980
0
           ->NewInstance(env->context())
981
0
           .ToLocal(&wrap)) {
982
0
    return;
983
0
  }
984
985
0
  BaseObjectPtr<WorkerCpuProfileTaker> taker =
986
0
      MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap);
987
988
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker),
989
0
                                        profile_id = profile_id,
990
0
                                        env](Environment* worker_env) mutable {
991
0
    bool found = false;
992
0
    auto json_out_stream = std::make_unique<node::JSONOutputStream>();
993
0
    CpuProfile* profile = worker_env->StopCpuProfile(profile_id);
994
0
    if (profile) {
995
0
      profile->Serialize(json_out_stream.get(),
996
0
                         CpuProfile::SerializationFormat::kJSON);
997
0
      profile->Delete();
998
0
      found = true;
999
0
    }
1000
0
    env->SetImmediateThreadsafe(
1001
0
        [taker = std::move(taker),
1002
0
         json_out_stream = std::move(json_out_stream),
1003
0
         found](Environment* env) mutable {
1004
0
          Isolate* isolate = env->isolate();
1005
0
          HandleScope handle_scope(isolate);
1006
0
          Context::Scope context_scope(env->context());
1007
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
1008
0
          Local<Value> argv[] = {
1009
0
              Null(isolate),       // error
1010
0
              Undefined(isolate),  // profile
1011
0
          };
1012
0
          if (found) {
1013
0
            Local<Value> result;
1014
0
            if (!ToV8Value(env->context(),
1015
0
                           json_out_stream->out_stream().str(),
1016
0
                           isolate)
1017
0
                     .ToLocal(&result)) {
1018
0
              return;
1019
0
            }
1020
0
            argv[1] = result;
1021
0
          } else {
1022
0
            argv[0] =
1023
0
                ERR_CPU_PROFILE_NOT_STARTED(isolate, "CPU profile not started");
1024
0
          }
1025
0
          taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
1026
0
        },
1027
0
        CallbackFlags::kUnrefed);
1028
0
  });
1029
1030
0
  if (scheduled) {
1031
0
    args.GetReturnValue().Set(wrap);
1032
0
  }
1033
0
}
1034
1035
class WorkerHeapProfileTaker final : public AsyncWrap {
1036
 public:
1037
  WorkerHeapProfileTaker(Environment* env, Local<Object> obj)
1038
0
      : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERHEAPPROFILE) {}
1039
1040
  SET_NO_MEMORY_INFO()
1041
  SET_MEMORY_INFO_NAME(WorkerHeapProfileTaker)
1042
  SET_SELF_SIZE(WorkerHeapProfileTaker)
1043
};
1044
1045
0
void Worker::StartHeapProfile(const FunctionCallbackInfo<Value>& args) {
1046
0
  Worker* w;
1047
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1048
0
  Environment* env = w->env();
1049
1050
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
1051
0
  Local<Object> wrap;
1052
0
  if (!env->worker_heap_profile_taker_template()
1053
0
           ->NewInstance(env->context())
1054
0
           .ToLocal(&wrap)) {
1055
0
    return;
1056
0
  }
1057
1058
0
  BaseObjectPtr<WorkerHeapProfileTaker> taker =
1059
0
      MakeDetachedBaseObject<WorkerHeapProfileTaker>(env, wrap);
1060
1061
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker),
1062
0
                                        env](Environment* worker_env) mutable {
1063
0
    v8::HeapProfiler* profiler = worker_env->isolate()->GetHeapProfiler();
1064
0
    bool success = profiler->StartSamplingHeapProfiler();
1065
0
    env->SetImmediateThreadsafe(
1066
0
        [taker = std::move(taker),
1067
0
         success = success](Environment* env) mutable {
1068
0
          Isolate* isolate = env->isolate();
1069
0
          HandleScope handle_scope(isolate);
1070
0
          Context::Scope context_scope(env->context());
1071
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
1072
0
          Local<Value> argv[] = {
1073
0
              Null(isolate),  // error
1074
0
          };
1075
0
          if (!success) {
1076
0
            argv[0] = ERR_HEAP_PROFILE_HAVE_BEEN_STARTED(
1077
0
                isolate, "heap profiler have been started");
1078
0
          }
1079
0
          taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
1080
0
        },
1081
0
        CallbackFlags::kUnrefed);
1082
0
  });
1083
1084
0
  if (scheduled) {
1085
0
    args.GetReturnValue().Set(wrap);
1086
0
  }
1087
0
}
1088
1089
static void buildHeapProfileNode(Isolate* isolate,
1090
                                 const AllocationProfile::Node* node,
1091
0
                                 JSONWriter* writer) {
1092
0
  size_t selfSize = 0;
1093
0
  for (const auto& allocation : node->allocations)
1094
0
    selfSize += allocation.size * allocation.count;
1095
1096
0
  writer->json_keyvalue("selfSize", selfSize);
1097
0
  writer->json_keyvalue("id", node->node_id);
1098
0
  writer->json_objectstart("callFrame");
1099
0
  writer->json_keyvalue("scriptId", node->script_id);
1100
0
  writer->json_keyvalue("lineNumber", node->line_number - 1);
1101
0
  writer->json_keyvalue("columnNumber", node->column_number - 1);
1102
0
  node::Utf8Value name(isolate, node->name);
1103
0
  node::Utf8Value script_name(isolate, node->script_name);
1104
0
  writer->json_keyvalue("functionName", *name);
1105
0
  writer->json_keyvalue("url", *script_name);
1106
0
  writer->json_objectend();
1107
1108
0
  writer->json_arraystart("children");
1109
0
  for (const auto* child : node->children) {
1110
0
    writer->json_start();
1111
0
    buildHeapProfileNode(isolate, child, writer);
1112
0
    writer->json_end();
1113
0
  }
1114
0
  writer->json_arrayend();
1115
0
}
1116
1117
0
static bool serializeProfile(Isolate* isolate, std::ostringstream& out_stream) {
1118
0
  HandleScope scope(isolate);
1119
0
  HeapProfiler* profiler = isolate->GetHeapProfiler();
1120
0
  std::unique_ptr<AllocationProfile> profile(profiler->GetAllocationProfile());
1121
0
  if (!profile) {
1122
0
    return false;
1123
0
  }
1124
0
  JSONWriter writer(out_stream, false);
1125
0
  writer.json_start();
1126
1127
0
  writer.json_arraystart("samples");
1128
0
  for (const auto& sample : profile->GetSamples()) {
1129
0
    writer.json_start();
1130
0
    writer.json_keyvalue("size", sample.size * sample.count);
1131
0
    writer.json_keyvalue("nodeId", sample.node_id);
1132
0
    writer.json_keyvalue("ordinal", static_cast<double>(sample.sample_id));
1133
0
    writer.json_end();
1134
0
  }
1135
0
  writer.json_arrayend();
1136
1137
0
  writer.json_objectstart("head");
1138
0
  buildHeapProfileNode(isolate, profile->GetRootNode(), &writer);
1139
0
  writer.json_objectend();
1140
1141
0
  writer.json_end();
1142
0
  profiler->StopSamplingHeapProfiler();
1143
0
  return true;
1144
0
}
1145
1146
0
void Worker::StopHeapProfile(const FunctionCallbackInfo<Value>& args) {
1147
0
  Worker* w;
1148
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1149
1150
0
  Environment* env = w->env();
1151
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
1152
0
  Local<Object> wrap;
1153
0
  if (!env->worker_heap_profile_taker_template()
1154
0
           ->NewInstance(env->context())
1155
0
           .ToLocal(&wrap)) {
1156
0
    return;
1157
0
  }
1158
1159
0
  BaseObjectPtr<WorkerHeapProfileTaker> taker =
1160
0
      MakeDetachedBaseObject<WorkerHeapProfileTaker>(env, wrap);
1161
1162
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker),
1163
0
                                        env](Environment* worker_env) mutable {
1164
0
    std::ostringstream out_stream;
1165
0
    bool success = serializeProfile(worker_env->isolate(), out_stream);
1166
0
    env->SetImmediateThreadsafe(
1167
0
        [taker = std::move(taker),
1168
0
         out_stream = std::move(out_stream),
1169
0
         success = success](Environment* env) mutable {
1170
0
          Isolate* isolate = env->isolate();
1171
0
          HandleScope handle_scope(isolate);
1172
0
          Context::Scope context_scope(env->context());
1173
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get());
1174
0
          Local<Value> argv[] = {
1175
0
              Null(isolate),       // error
1176
0
              Undefined(isolate),  // profile
1177
0
          };
1178
0
          if (success) {
1179
0
            Local<Value> result;
1180
0
            if (!ToV8Value(env->context(), out_stream.str(), isolate)
1181
0
                     .ToLocal(&result)) {
1182
0
              return;
1183
0
            }
1184
0
            argv[1] = result;
1185
0
          } else {
1186
0
            argv[0] = ERR_HEAP_PROFILE_NOT_STARTED(isolate,
1187
0
                                                   "heap profile not started");
1188
0
          }
1189
0
          taker->MakeCallback(env->ondone_string(), arraysize(argv), argv);
1190
0
        },
1191
0
        CallbackFlags::kUnrefed);
1192
0
  });
1193
1194
0
  if (scheduled) {
1195
0
    args.GetReturnValue().Set(wrap);
1196
0
  }
1197
0
}
1198
class WorkerHeapStatisticsTaker : public AsyncWrap {
1199
 public:
1200
  WorkerHeapStatisticsTaker(Environment* env, Local<Object> obj)
1201
0
      : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERHEAPSTATISTICS) {}
1202
1203
  SET_NO_MEMORY_INFO()
1204
  SET_MEMORY_INFO_NAME(WorkerHeapStatisticsTaker)
1205
  SET_SELF_SIZE(WorkerHeapStatisticsTaker)
1206
};
1207
1208
0
void Worker::GetHeapStatistics(const FunctionCallbackInfo<Value>& args) {
1209
0
  Worker* w;
1210
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1211
1212
0
  Environment* env = w->env();
1213
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
1214
0
  Local<Object> wrap;
1215
0
  if (!env->worker_heap_statistics_taker_template()
1216
0
           ->NewInstance(env->context())
1217
0
           .ToLocal(&wrap)) {
1218
0
    return;
1219
0
  }
1220
1221
  // The created WorkerHeapStatisticsTaker is an object owned by main
1222
  // thread's Isolate, it can not be accessed by worker thread
1223
0
  std::unique_ptr<BaseObjectPtr<WorkerHeapStatisticsTaker>> taker =
1224
0
      std::make_unique<BaseObjectPtr<WorkerHeapStatisticsTaker>>(
1225
0
          MakeDetachedBaseObject<WorkerHeapStatisticsTaker>(env, wrap));
1226
1227
  // Interrupt the worker thread and take a snapshot, then schedule a call
1228
  // on the parent thread that turns that snapshot into a readable stream.
1229
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker),
1230
0
                                        env](Environment* worker_env) mutable {
1231
    // We create a unique pointer to HeapStatistics so that the actual object
1232
    // it's not copied in the lambda, but only the pointer is.
1233
0
    auto heap_stats = std::make_unique<HeapStatistics>();
1234
0
    worker_env->isolate()->GetHeapStatistics(heap_stats.get());
1235
1236
    // Here, the worker thread temporarily owns the WorkerHeapStatisticsTaker
1237
    // object.
1238
1239
0
    env->SetImmediateThreadsafe(
1240
0
        [taker = std::move(taker),
1241
0
         heap_stats = std::move(heap_stats)](Environment* env) mutable {
1242
0
          Isolate* isolate = env->isolate();
1243
0
          HandleScope handle_scope(isolate);
1244
0
          Context::Scope context_scope(env->context());
1245
1246
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker->get());
1247
1248
0
          auto tmpl = env->heap_statistics_template();
1249
0
          if (tmpl.IsEmpty()) {
1250
0
            std::string_view heap_stats_names[] = {
1251
0
                "total_heap_size",
1252
0
                "total_heap_size_executable",
1253
0
                "total_physical_size",
1254
0
                "total_available_size",
1255
0
                "used_heap_size",
1256
0
                "heap_size_limit",
1257
0
                "malloced_memory",
1258
0
                "peak_malloced_memory",
1259
0
                "does_zap_garbage",
1260
0
                "number_of_native_contexts",
1261
0
                "number_of_detached_contexts",
1262
0
                "total_global_handles_size",
1263
0
                "used_global_handles_size",
1264
0
                "external_memory",
1265
0
                "total_allocated_bytes",
1266
0
            };
1267
0
            tmpl = DictionaryTemplate::New(isolate, heap_stats_names);
1268
0
            env->set_heap_statistics_template(tmpl);
1269
0
          }
1270
1271
          // Define an array of property values
1272
0
          MaybeLocal<Value> heap_stats_values[] = {
1273
0
              Number::New(isolate, heap_stats->total_heap_size()),
1274
0
              Number::New(isolate, heap_stats->total_heap_size_executable()),
1275
0
              Number::New(isolate, heap_stats->total_physical_size()),
1276
0
              Number::New(isolate, heap_stats->total_available_size()),
1277
0
              Number::New(isolate, heap_stats->used_heap_size()),
1278
0
              Number::New(isolate, heap_stats->heap_size_limit()),
1279
0
              Number::New(isolate, heap_stats->malloced_memory()),
1280
0
              Number::New(isolate, heap_stats->peak_malloced_memory()),
1281
0
              Boolean::New(isolate, heap_stats->does_zap_garbage()),
1282
0
              Number::New(isolate, heap_stats->number_of_native_contexts()),
1283
0
              Number::New(isolate, heap_stats->number_of_detached_contexts()),
1284
0
              Number::New(isolate, heap_stats->total_global_handles_size()),
1285
0
              Number::New(isolate, heap_stats->used_global_handles_size()),
1286
0
              Number::New(isolate, heap_stats->external_memory()),
1287
0
              Number::New(isolate, heap_stats->total_allocated_bytes())};
1288
1289
0
          Local<Object> obj;
1290
0
          if (!NewDictionaryInstanceNullProto(
1291
0
                   env->context(), tmpl, heap_stats_values)
1292
0
                   .ToLocal(&obj)) {
1293
0
            return;
1294
0
          }
1295
0
          Local<Value> args[] = {obj};
1296
0
          taker->get()->MakeCallback(
1297
0
              env->ondone_string(), arraysize(args), args);
1298
          // implicitly delete `taker`
1299
0
        },
1300
0
        CallbackFlags::kUnrefed);
1301
1302
    // Now, the lambda is delivered to the main thread, as a result, the
1303
    // WorkerHeapStatisticsTaker object is delivered to the main thread, too.
1304
0
  });
1305
1306
0
  if (scheduled) {
1307
0
    args.GetReturnValue().Set(wrap);
1308
0
  } else {
1309
0
    args.GetReturnValue().Set(Local<Object>());
1310
0
  }
1311
0
}
1312
1313
0
void Worker::GetResourceLimits(const FunctionCallbackInfo<Value>& args) {
1314
0
  Worker* w;
1315
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1316
0
  args.GetReturnValue().Set(w->GetResourceLimits(args.GetIsolate()));
1317
0
}
1318
1319
0
Local<Float64Array> Worker::GetResourceLimits(Isolate* isolate) const {
1320
0
  Local<ArrayBuffer> ab = ArrayBuffer::New(isolate, sizeof(resource_limits_));
1321
1322
0
  memcpy(ab->Data(), resource_limits_, sizeof(resource_limits_));
1323
0
  return Float64Array::New(ab, 0, kTotalResourceLimitCount);
1324
0
}
1325
1326
void Worker::Exit(ExitCode code,
1327
                  const char* error_code,
1328
0
                  const char* error_message) {
1329
0
  Mutex::ScopedLock lock(mutex_);
1330
0
  Debug(this,
1331
0
        "Worker %llu called Exit(%d, %s, %s)",
1332
0
        thread_id_.id,
1333
0
        static_cast<int>(code),
1334
0
        error_code,
1335
0
        error_message);
1336
1337
0
  if (error_code != nullptr) {
1338
0
    custom_error_ = error_code;
1339
0
    custom_error_str_ = error_message;
1340
0
  }
1341
1342
0
  if (env_ != nullptr) {
1343
0
    exit_code_ = code;
1344
0
    Stop(env_);
1345
0
  } else {
1346
0
    stopped_ = true;
1347
0
  }
1348
0
}
1349
1350
0
bool Worker::IsNotIndicativeOfMemoryLeakAtExit() const {
1351
  // Worker objects always stay alive as long as the child thread, regardless
1352
  // of whether they are being referenced in the parent thread.
1353
0
  return true;
1354
0
}
1355
1356
class WorkerHeapSnapshotTaker : public AsyncWrap {
1357
 public:
1358
  WorkerHeapSnapshotTaker(Environment* env, Local<Object> obj)
1359
0
    : AsyncWrap(env, obj, AsyncWrap::PROVIDER_WORKERHEAPSNAPSHOT) {}
1360
1361
  SET_NO_MEMORY_INFO()
1362
  SET_MEMORY_INFO_NAME(WorkerHeapSnapshotTaker)
1363
  SET_SELF_SIZE(WorkerHeapSnapshotTaker)
1364
};
1365
1366
0
void Worker::TakeHeapSnapshot(const FunctionCallbackInfo<Value>& args) {
1367
0
  Worker* w;
1368
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1369
0
  CHECK_EQ(args.Length(), 1);
1370
0
  auto options = heap::GetHeapSnapshotOptions(args[0]);
1371
1372
0
  Debug(w, "Worker %llu taking heap snapshot", w->thread_id_.id);
1373
1374
0
  Environment* env = w->env();
1375
0
  AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w);
1376
0
  Local<Object> wrap;
1377
0
  if (!env->worker_heap_snapshot_taker_template()
1378
0
      ->NewInstance(env->context()).ToLocal(&wrap)) {
1379
0
    return;
1380
0
  }
1381
1382
  // The created WorkerHeapSnapshotTaker is an object owned by main
1383
  // thread's Isolate, it can not be accessed by worker thread
1384
0
  std::unique_ptr<BaseObjectPtr<WorkerHeapSnapshotTaker>> taker =
1385
0
      std::make_unique<BaseObjectPtr<WorkerHeapSnapshotTaker>>(
1386
0
          MakeDetachedBaseObject<WorkerHeapSnapshotTaker>(env, wrap));
1387
1388
  // Interrupt the worker thread and take a snapshot, then schedule a call
1389
  // on the parent thread that turns that snapshot into a readable stream.
1390
0
  bool scheduled = w->RequestInterrupt([taker = std::move(taker), env, options](
1391
0
                                           Environment* worker_env) mutable {
1392
0
    heap::HeapSnapshotPointer snapshot{
1393
0
        worker_env->isolate()->GetHeapProfiler()->TakeHeapSnapshot(options)};
1394
0
    CHECK(snapshot);
1395
1396
    // Here, the worker thread temporarily owns the WorkerHeapSnapshotTaker
1397
    // object.
1398
1399
0
    env->SetImmediateThreadsafe(
1400
0
        [taker = std::move(taker),
1401
0
         snapshot = std::move(snapshot)](Environment* env) mutable {
1402
0
          HandleScope handle_scope(env->isolate());
1403
0
          Context::Scope context_scope(env->context());
1404
1405
0
          AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker->get());
1406
0
          BaseObjectPtr<AsyncWrap> stream =
1407
0
              heap::CreateHeapSnapshotStream(env, std::move(snapshot));
1408
0
          Local<Value> args[] = {stream->object()};
1409
0
          taker->get()->MakeCallback(
1410
0
              env->ondone_string(), arraysize(args), args);
1411
          // implicitly delete `taker`
1412
0
        },
1413
0
        CallbackFlags::kUnrefed);
1414
1415
    // Now, the lambda is delivered to the main thread, as a result, the
1416
    // WorkerHeapSnapshotTaker object is delivered to the main thread, too.
1417
0
  });
1418
1419
0
  if (scheduled) {
1420
0
    args.GetReturnValue().Set(wrap);
1421
0
  } else {
1422
0
    args.GetReturnValue().Set(Local<Object>());
1423
0
  }
1424
0
}
1425
1426
0
void Worker::LoopIdleTime(const FunctionCallbackInfo<Value>& args) {
1427
0
  Worker* w;
1428
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1429
1430
0
  Mutex::ScopedLock lock(w->mutex_);
1431
  // Using w->is_stopped() here leads to a deadlock, and checking is_stopped()
1432
  // before locking the mutex is a race condition. So manually do the same
1433
  // check.
1434
0
  if (w->stopped_ || w->env_ == nullptr)
1435
0
    return args.GetReturnValue().Set(-1);
1436
1437
0
  uint64_t idle_time = uv_metrics_idle_time(w->env_->event_loop());
1438
0
  args.GetReturnValue().Set(1.0 * idle_time / 1e6);
1439
0
}
1440
1441
0
void Worker::LoopStartTime(const FunctionCallbackInfo<Value>& args) {
1442
0
  Worker* w;
1443
0
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
1444
1445
0
  Mutex::ScopedLock lock(w->mutex_);
1446
  // Using w->is_stopped() here leads to a deadlock, and checking is_stopped()
1447
  // before locking the mutex is a race condition. So manually do the same
1448
  // check.
1449
0
  if (w->stopped_ || w->env_ == nullptr)
1450
0
    return args.GetReturnValue().Set(-1);
1451
1452
0
  double loop_start_time = w->env_->performance_state()->milestones[
1453
0
      node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START];
1454
0
  CHECK_GE(loop_start_time, 0);
1455
0
  args.GetReturnValue().Set(loop_start_time / 1e6);
1456
0
}
1457
1458
namespace {
1459
1460
// Return the MessagePort that is global for this Environment and communicates
1461
// with the internal [kPort] port of the JS Worker class in the parent thread.
1462
0
void GetEnvMessagePort(const FunctionCallbackInfo<Value>& args) {
1463
0
  Environment* env = Environment::GetCurrent(args);
1464
0
  Local<Object> port = env->message_port();
1465
0
  CHECK_IMPLIES(!env->is_main_thread(), !port.IsEmpty());
1466
0
  if (!port.IsEmpty()) {
1467
0
    args.GetReturnValue().Set(port);
1468
0
  }
1469
0
}
1470
1471
void CreateWorkerPerIsolateProperties(IsolateData* isolate_data,
1472
35
                                      Local<ObjectTemplate> target) {
1473
35
  Isolate* isolate = isolate_data->isolate();
1474
1475
35
  {
1476
35
    Local<FunctionTemplate> w = NewFunctionTemplate(isolate, Worker::New);
1477
1478
35
    w->InstanceTemplate()->SetInternalFieldCount(
1479
35
        Worker::kInternalFieldCount);
1480
35
    w->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1481
1482
35
    SetProtoMethod(isolate, w, "startThread", Worker::StartThread);
1483
35
    SetProtoMethod(isolate, w, "stopThread", Worker::StopThread);
1484
35
    SetProtoMethod(isolate, w, "hasRef", Worker::HasRef);
1485
35
    SetProtoMethod(isolate, w, "ref", Worker::Ref);
1486
35
    SetProtoMethod(isolate, w, "unref", Worker::Unref);
1487
35
    SetProtoMethod(isolate, w, "getResourceLimits", Worker::GetResourceLimits);
1488
35
    SetProtoMethod(isolate, w, "takeHeapSnapshot", Worker::TakeHeapSnapshot);
1489
35
    SetProtoMethod(isolate, w, "loopIdleTime", Worker::LoopIdleTime);
1490
35
    SetProtoMethod(isolate, w, "loopStartTime", Worker::LoopStartTime);
1491
35
    SetProtoMethod(isolate, w, "getHeapStatistics", Worker::GetHeapStatistics);
1492
35
    SetProtoMethod(isolate, w, "cpuUsage", Worker::CpuUsage);
1493
35
    SetProtoMethod(isolate, w, "startCpuProfile", Worker::StartCpuProfile);
1494
35
    SetProtoMethod(isolate, w, "stopCpuProfile", Worker::StopCpuProfile);
1495
35
    SetProtoMethod(isolate, w, "startHeapProfile", Worker::StartHeapProfile);
1496
35
    SetProtoMethod(isolate, w, "stopHeapProfile", Worker::StopHeapProfile);
1497
1498
35
    SetConstructorFunction(isolate, target, "Worker", w);
1499
35
  }
1500
1501
35
  {
1502
35
    Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr);
1503
1504
35
    wst->InstanceTemplate()->SetInternalFieldCount(
1505
35
        WorkerHeapSnapshotTaker::kInternalFieldCount);
1506
35
    wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1507
1508
35
    Local<String> wst_string =
1509
35
        FIXED_ONE_BYTE_STRING(isolate, "WorkerHeapSnapshotTaker");
1510
35
    wst->SetClassName(wst_string);
1511
35
    isolate_data->set_worker_heap_snapshot_taker_template(
1512
35
        wst->InstanceTemplate());
1513
35
  }
1514
1515
35
  {
1516
35
    Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr);
1517
1518
35
    wst->InstanceTemplate()->SetInternalFieldCount(
1519
35
        WorkerHeapSnapshotTaker::kInternalFieldCount);
1520
35
    wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1521
1522
35
    Local<String> wst_string =
1523
35
        FIXED_ONE_BYTE_STRING(isolate, "WorkerHeapStatisticsTaker");
1524
35
    wst->SetClassName(wst_string);
1525
35
    isolate_data->set_worker_heap_statistics_taker_template(
1526
35
        wst->InstanceTemplate());
1527
35
  }
1528
1529
35
  {
1530
35
    Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr);
1531
1532
35
    wst->InstanceTemplate()->SetInternalFieldCount(
1533
35
        WorkerCpuUsageTaker::kInternalFieldCount);
1534
35
    wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1535
1536
35
    Local<String> wst_string =
1537
35
        FIXED_ONE_BYTE_STRING(isolate, "WorkerCpuUsageTaker");
1538
35
    wst->SetClassName(wst_string);
1539
35
    isolate_data->set_worker_cpu_usage_taker_template(wst->InstanceTemplate());
1540
35
  }
1541
1542
35
  {
1543
35
    Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr);
1544
1545
35
    wst->InstanceTemplate()->SetInternalFieldCount(
1546
35
        WorkerCpuProfileTaker::kInternalFieldCount);
1547
35
    wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1548
1549
35
    Local<String> wst_string =
1550
35
        FIXED_ONE_BYTE_STRING(isolate, "WorkerCpuProfileTaker");
1551
35
    wst->SetClassName(wst_string);
1552
35
    isolate_data->set_worker_cpu_profile_taker_template(
1553
35
        wst->InstanceTemplate());
1554
35
  }
1555
1556
35
  {
1557
35
    Local<FunctionTemplate> wst = NewFunctionTemplate(isolate, nullptr);
1558
1559
35
    wst->InstanceTemplate()->SetInternalFieldCount(
1560
35
        WorkerHeapProfileTaker::kInternalFieldCount);
1561
35
    wst->Inherit(AsyncWrap::GetConstructorTemplate(isolate_data));
1562
1563
35
    Local<String> wst_string =
1564
35
        FIXED_ONE_BYTE_STRING(isolate, "WorkerHeapProfileTaker");
1565
35
    wst->SetClassName(wst_string);
1566
35
    isolate_data->set_worker_heap_profile_taker_template(
1567
35
        wst->InstanceTemplate());
1568
35
  }
1569
1570
35
  SetMethod(isolate, target, "getEnvMessagePort", GetEnvMessagePort);
1571
35
}
1572
1573
void CreateWorkerPerContextProperties(Local<Object> target,
1574
                                      Local<Value> unused,
1575
                                      Local<Context> context,
1576
35
                                      void* priv) {
1577
35
  Environment* env = Environment::GetCurrent(context);
1578
35
  Isolate* isolate = env->isolate();
1579
1580
35
  target
1581
35
      ->Set(env->context(),
1582
35
            env->thread_id_string(),
1583
35
            Number::New(isolate, static_cast<double>(env->thread_id())))
1584
35
      .Check();
1585
1586
35
  target
1587
35
      ->Set(env->context(),
1588
35
            env->thread_name_string(),
1589
35
            String::NewFromUtf8(isolate,
1590
35
                                env->thread_name().data(),
1591
35
                                NewStringType::kNormal,
1592
35
                                env->thread_name().size())
1593
35
                .ToLocalChecked())
1594
35
      .Check();
1595
1596
35
  target
1597
35
      ->Set(env->context(),
1598
35
            FIXED_ONE_BYTE_STRING(isolate, "isMainThread"),
1599
35
            Boolean::New(isolate, env->is_main_thread()))
1600
35
      .Check();
1601
1602
35
  Worker* worker = env->isolate_data()->worker_context();
1603
35
  bool is_internal = worker != nullptr && worker->is_internal();
1604
1605
  // Set the is_internal property
1606
35
  target
1607
35
      ->Set(env->context(),
1608
35
            FIXED_ONE_BYTE_STRING(isolate, "isInternalThread"),
1609
35
            Boolean::New(isolate, is_internal))
1610
35
      .Check();
1611
1612
35
  target
1613
35
      ->Set(env->context(),
1614
35
            FIXED_ONE_BYTE_STRING(isolate, "ownsProcessState"),
1615
35
            Boolean::New(isolate, env->owns_process_state()))
1616
35
      .Check();
1617
1618
35
  if (!env->is_main_thread()) {
1619
0
    target
1620
0
        ->Set(env->context(),
1621
0
              FIXED_ONE_BYTE_STRING(isolate, "resourceLimits"),
1622
0
              env->worker_context()->GetResourceLimits(isolate))
1623
0
        .Check();
1624
0
  }
1625
1626
35
  NODE_DEFINE_CONSTANT(target, kMaxYoungGenerationSizeMb);
1627
35
  NODE_DEFINE_CONSTANT(target, kMaxOldGenerationSizeMb);
1628
35
  NODE_DEFINE_CONSTANT(target, kCodeRangeSizeMb);
1629
35
  NODE_DEFINE_CONSTANT(target, kStackSizeMb);
1630
35
  NODE_DEFINE_CONSTANT(target, kTotalResourceLimitCount);
1631
35
}
1632
1633
0
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
1634
0
  registry->Register(GetEnvMessagePort);
1635
0
  registry->Register(Worker::New);
1636
0
  registry->Register(Worker::StartThread);
1637
0
  registry->Register(Worker::StopThread);
1638
0
  registry->Register(Worker::HasRef);
1639
0
  registry->Register(Worker::Ref);
1640
0
  registry->Register(Worker::Unref);
1641
0
  registry->Register(Worker::GetResourceLimits);
1642
0
  registry->Register(Worker::TakeHeapSnapshot);
1643
0
  registry->Register(Worker::LoopIdleTime);
1644
0
  registry->Register(Worker::LoopStartTime);
1645
0
  registry->Register(Worker::GetHeapStatistics);
1646
0
  registry->Register(Worker::CpuUsage);
1647
0
  registry->Register(Worker::StartCpuProfile);
1648
0
  registry->Register(Worker::StopCpuProfile);
1649
0
  registry->Register(Worker::StartHeapProfile);
1650
0
  registry->Register(Worker::StopHeapProfile);
1651
0
}
1652
1653
}  // anonymous namespace
1654
}  // namespace worker
1655
}  // namespace node
1656
1657
NODE_BINDING_CONTEXT_AWARE_INTERNAL(
1658
    worker, node::worker::CreateWorkerPerContextProperties)
1659
NODE_BINDING_PER_ISOLATE_INIT(worker,
1660
                              node::worker::CreateWorkerPerIsolateProperties)
1661
NODE_BINDING_EXTERNAL_REFERENCE(worker,
1662
                                node::worker::RegisterExternalReferences)