GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage-daily/nodes/benchmark/out/../src/node_worker.cc Lines: 303 308 98.4 %
Date: 2019-09-15 22:29:17 Branches: 123 172 71.5 %

Line Branch Exec Source
1
#include "node_worker.h"
2
#include "debug_utils.h"
3
#include "memory_tracker-inl.h"
4
#include "node_errors.h"
5
#include "node_buffer.h"
6
#include "node_options-inl.h"
7
#include "node_perf.h"
8
#include "util-inl.h"
9
#include "async_wrap-inl.h"
10
11
#if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR
12
#include "inspector/worker_inspector.h"  // ParentInspectorHandle
13
#endif
14
15
#include <memory>
16
#include <string>
17
#include <vector>
18
19
using node::options_parser::kDisallowedInEnvironment;
20
using v8::Array;
21
using v8::ArrayBuffer;
22
using v8::Boolean;
23
using v8::Context;
24
using v8::Function;
25
using v8::FunctionCallbackInfo;
26
using v8::FunctionTemplate;
27
using v8::HandleScope;
28
using v8::Integer;
29
using v8::Isolate;
30
using v8::Local;
31
using v8::Locker;
32
using v8::MaybeLocal;
33
using v8::Number;
34
using v8::Object;
35
using v8::SealHandleScope;
36
using v8::String;
37
using v8::Value;
38
39
namespace node {
40
namespace worker {
41
42
namespace {
43
44
#if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR
45
209
void WaitForWorkerInspectorToStop(Environment* child) {
46
209
  child->inspector_agent()->WaitForDisconnect();
47
209
  child->inspector_agent()->Stop();
48
209
}
49
#endif
50
51
}  // anonymous namespace
52
53
216
Worker::Worker(Environment* env,
54
               Local<Object> wrap,
55
               const std::string& url,
56
               std::shared_ptr<PerIsolateOptions> per_isolate_opts,
57
               std::vector<std::string>&& exec_argv)
58
    : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_WORKER),
59
      per_isolate_opts_(per_isolate_opts),
60
      exec_argv_(exec_argv),
61
216
      platform_(env->isolate_data()->platform()),
62
      array_buffer_allocator_(ArrayBufferAllocator::Create()),
63
216
      start_profiler_idle_notifier_(env->profiler_idle_notifier_started()),
64
216
      thread_id_(Environment::AllocateThreadId()),
65
864
      env_vars_(env->env_vars()) {
66
216
  Debug(this, "Creating new worker instance with thread id %llu", thread_id_);
67
68
  // Set up everything that needs to be set up in the parent environment.
69
216
  parent_port_ = MessagePort::New(env, env->context());
70
216
  if (parent_port_ == nullptr) {
71
    // This can happen e.g. because execution is terminating.
72
216
    return;
73
  }
74
75
216
  child_port_data_ = std::make_unique<MessagePortData>(nullptr);
76
216
  MessagePort::Entangle(parent_port_, child_port_data_.get());
77
78
216
  object()->Set(env->context(),
79
                env->message_port_string(),
80
1296
                parent_port_->object()).Check();
81
82
216
  object()->Set(env->context(),
83
                env->thread_id_string(),
84
1296
                Number::New(env->isolate(), static_cast<double>(thread_id_)))
85
432
      .Check();
86
87
#if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR
88
432
  inspector_parent_handle_ =
89
216
      env->inspector_agent()->GetParentHandle(thread_id_, url);
90
#endif
91
92
216
  argv_ = std::vector<std::string>{env->argv()[0]};
93
  // Mark this Worker object as weak until we actually start the thread.
94
216
  MakeWeak();
95
96
216
  Debug(this, "Preparation for worker %llu finished", thread_id_);
97
}
98
99
1846
bool Worker::is_stopped() const {
100
1846
  Mutex::ScopedLock lock(mutex_);
101
1847
  if (env_ != nullptr)
102
1230
    return env_->is_stopping();
103
617
  return stopped_;
104
}
105
106
3
std::shared_ptr<ArrayBufferAllocator> Worker::array_buffer_allocator() {
107
3
  return array_buffer_allocator_;
108
}
109
110
// This class contains data that is only relevant to the child thread itself,
111
// and only while it is running.
112
// (Eventually, the Environment instance should probably also be moved here.)
113
class WorkerThreadData {
114
 public:
115
215
  explicit WorkerThreadData(Worker* w)
116
215
    : w_(w) {
117
215
    CHECK_EQ(uv_loop_init(&loop_), 0);
118
119
215
    Isolate* isolate = NewIsolate(w->array_buffer_allocator_.get(), &loop_);
120
215
    CHECK_NOT_NULL(isolate);
121
122
    {
123
215
      Locker locker(isolate);
124
430
      Isolate::Scope isolate_scope(isolate);
125
215
      isolate->SetStackLimit(w_->stack_base_);
126
127
430
      HandleScope handle_scope(isolate);
128
      isolate_data_.reset(CreateIsolateData(isolate,
129
                                            &loop_,
130
                                            w_->platform_,
131
215
                                            w->array_buffer_allocator_.get()));
132
215
      CHECK(isolate_data_);
133
215
      if (w_->per_isolate_opts_)
134
224
        isolate_data_->set_options(std::move(w_->per_isolate_opts_));
135
    }
136
137
215
    Mutex::ScopedLock lock(w_->mutex_);
138
215
    w_->isolate_ = isolate;
139
215
  }
140
141
428
  ~WorkerThreadData() {
142
213
    Debug(w_, "Worker %llu dispose isolate", w_->thread_id_);
143
    Isolate* isolate;
144
    {
145
214
      Mutex::ScopedLock lock(w_->mutex_);
146
215
      isolate = w_->isolate_;
147
215
      w_->isolate_ = nullptr;
148
    }
149
150
215
    w_->platform_->CancelPendingDelayedTasks(isolate);
151
152
215
    bool platform_finished = false;
153
154
215
    isolate_data_.reset();
155
156
645
    w_->platform_->AddIsolateFinishedCallback(isolate, [](void* data) {
157
215
      *static_cast<bool*>(data) = true;
158
860
    }, &platform_finished);
159
215
    w_->platform_->UnregisterIsolate(isolate);
160
161
215
    isolate->Dispose();
162
163
    // Wait until the platform has cleaned up all relevant resources.
164
645
    while (!platform_finished)
165
215
      uv_run(&loop_, UV_RUN_ONCE);
166
167
215
    CheckedUvLoopClose(&loop_);
168
215
  }
169
170
 private:
171
  Worker* const w_;
172
  uv_loop_t loop_;
173
  DeleteFnPtr<IsolateData, FreeIsolateData> isolate_data_;
174
175
  friend class Worker;
176
};
177
178
215
void Worker::Run() {
179
215
  std::string name = "WorkerThread ";
180
215
  name += std::to_string(thread_id_);
181

432
  TRACE_EVENT_METADATA1(
182
      "__metadata", "thread_name", "name",
183
      TRACE_STR_COPY(name.c_str()));
184
215
  CHECK_NOT_NULL(platform_);
185
186
215
  Debug(this, "Creating isolate for worker with id %llu", thread_id_);
187
188
418
  WorkerThreadData data(this);
189
190
215
  Debug(this, "Starting worker with id %llu", thread_id_);
191
  {
192
215
    Locker locker(isolate_);
193
417
    Isolate::Scope isolate_scope(isolate_);
194
417
    SealHandleScope outer_seal(isolate_);
195
#if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR
196
215
    bool inspector_started = false;
197
#endif
198
199
418
    DeleteFnPtr<Environment, FreeEnvironment> env_;
200
215
    OnScopeLeave cleanup_env([&]() {
201
430
      if (!env_) return;
202
209
      env_->set_can_call_into_js(false);
203
      Isolate::DisallowJavascriptExecutionScope disallow_js(isolate_,
204
209
          Isolate::DisallowJavascriptExecutionScope::THROW_ON_FAILURE);
205
206
      // Grab the parent-to-child channel and render is unusable.
207
      MessagePort* child_port;
208
      {
209
209
        Mutex::ScopedLock lock(mutex_);
210
209
        child_port = child_port_;
211
209
        child_port_ = nullptr;
212
      }
213
214
      {
215
209
        Context::Scope context_scope(env_->context());
216
209
        if (child_port != nullptr)
217
406
          child_port->Close();
218
        {
219
209
          Mutex::ScopedLock lock(mutex_);
220
209
          stopped_ = true;
221
209
          this->env_ = nullptr;
222
        }
223
209
        env_->thread_stopper()->set_stopped(true);
224
209
        env_->stop_sub_worker_contexts();
225
209
        env_->RunCleanup();
226
209
        RunAtExit(env_.get());
227
#if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR
228
209
        if (inspector_started)
229
209
          WaitForWorkerInspectorToStop(env_.get());
230
#endif
231
232
        // This call needs to be made while the `Environment` is still alive
233
        // because we assume that it is available for async tracking in the
234
        // NodePlatform implementation.
235
209
        platform_->DrainTasks(isolate_);
236
      }
237
627
    });
238
239
215
    if (is_stopped()) return;
240
    {
241
211
      HandleScope handle_scope(isolate_);
242
211
      Local<Context> context = NewContext(isolate_);
243
244
211
      if (is_stopped()) return;
245
209
      CHECK(!context.IsEmpty());
246
203
      Context::Scope context_scope(context);
247
      {
248
        // TODO(addaleax): Use CreateEnvironment(), or generally another
249
        // public API.
250
        env_.reset(new Environment(data.isolate_data_.get(),
251
                                   context,
252
209
                                   std::move(argv_),
253
209
                                   std::move(exec_argv_),
254
                                   Environment::kNoFlags,
255
627
                                   thread_id_));
256
209
        CHECK_NOT_NULL(env_);
257
208
        env_->set_env_vars(std::move(env_vars_));
258
209
        env_->set_abort_on_uncaught_exception(false);
259
209
        env_->set_worker_context(this);
260
261
208
        env_->InitializeLibuv(start_profiler_idle_notifier_);
262
      }
263
      {
264
209
        Mutex::ScopedLock lock(mutex_);
265
209
        if (stopped_) return;
266
209
        this->env_ = env_.get();
267
      }
268
209
      Debug(this, "Created Environment for worker with id %llu", thread_id_);
269
208
      if (is_stopped()) return;
270
      {
271
209
        env_->InitializeDiagnostics();
272
#if NODE_USE_V8_PLATFORM && HAVE_INSPECTOR
273
209
        env_->InitializeInspector(inspector_parent_handle_.release());
274
209
        inspector_started = true;
275
#endif
276
209
        HandleScope handle_scope(isolate_);
277
418
        AsyncCallbackScope callback_scope(env_.get());
278
209
        env_->async_hooks()->push_async_ids(1, 0);
279
418
        if (!env_->RunBootstrapping().IsEmpty()) {
280
203
          CreateEnvMessagePort(env_.get());
281
203
          if (is_stopped()) return;
282
203
          Debug(this, "Created message port for worker %llu", thread_id_);
283
203
          USE(StartExecution(env_.get(), "internal/main/worker_thread"));
284
        }
285
286
209
        env_->async_hooks()->pop_async_id(1);
287
288
418
        Debug(this, "Loaded environment for worker %llu", thread_id_);
289
      }
290
291
209
      if (is_stopped()) return;
292
      {
293
203
        SealHandleScope seal(isolate_);
294
        bool more;
295
        env_->performance_state()->Mark(
296
203
            node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
297
147
        do {
298
203
          if (is_stopped()) break;
299
203
          uv_run(&data.loop_, UV_RUN_DEFAULT);
300
203
          if (is_stopped()) break;
301
302
147
          platform_->DrainTasks(isolate_);
303
304
147
          more = uv_loop_alive(&data.loop_);
305

147
          if (more && !is_stopped()) continue;
306
307
147
          EmitBeforeExit(env_.get());
308
309
          // Emit `beforeExit` if the loop became alive either after emitting
310
          // event, or after running some callbacks.
311
147
          more = uv_loop_alive(&data.loop_);
312

147
        } while (more == true && !is_stopped());
313
        env_->performance_state()->Mark(
314
203
            node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
315
203
      }
316
    }
317
318
    {
319
      int exit_code;
320
203
      bool stopped = is_stopped();
321
203
      if (!stopped)
322
147
        exit_code = EmitExit(env_.get());
323
203
      Mutex::ScopedLock lock(mutex_);
324

203
      if (exit_code_ == 0 && !stopped)
325
144
        exit_code_ = exit_code;
326
327
#if HAVE_INSPECTOR
328
203
      profiler::EndStartedProfilers(env_.get());
329
#endif
330
      Debug(this, "Exiting thread for worker %llu with exit code %d",
331
406
            thread_id_, exit_code_);
332
203
    }
333
  }
334
335
406
  Debug(this, "Worker %llu thread stops", thread_id_);
336
}
337
338
203
void Worker::CreateEnvMessagePort(Environment* env) {
339
203
  HandleScope handle_scope(isolate_);
340
406
  Mutex::ScopedLock lock(mutex_);
341
  // Set up the message channel for receiving messages in the child.
342
  child_port_ = MessagePort::New(env,
343
                                 env->context(),
344
203
                                 std::move(child_port_data_));
345
  // MessagePort::New() may return nullptr if execution is terminated
346
  // within it.
347
203
  if (child_port_ != nullptr)
348
406
    env->set_message_port(child_port_->object(isolate_));
349
203
}
350
351
215
void Worker::JoinThread() {
352
215
  if (thread_joined_)
353
210
    return;
354
215
  CHECK_EQ(uv_thread_join(&tid_), 0);
355
215
  thread_joined_ = true;
356
357
215
  env()->remove_sub_worker_context(this);
358
215
  on_thread_finished_.Uninstall();
359
360
  {
361
215
    HandleScope handle_scope(env()->isolate());
362
215
    Context::Scope context_scope(env()->context());
363
364
    // Reset the parent port as we're closing it now anyway.
365
215
    object()->Set(env()->context(),
366
                  env()->message_port_string(),
367
1290
                  Undefined(env()->isolate())).Check();
368
369
215
    Local<Value> code = Integer::New(env()->isolate(), exit_code_);
370
425
    MakeCallback(env()->onexit_string(), 1, &code);
371
  }
372
373
  // We cleared all libuv handles bound to this Worker above,
374
  // the C++ object is no longer needed for anything now.
375
210
  MakeWeak();
376
}
377
378
570
Worker::~Worker() {
379
190
  Mutex::ScopedLock lock(mutex_);
380
381
190
  CHECK(stopped_);
382
190
  CHECK_NULL(env_);
383
190
  CHECK(thread_joined_);
384
385
380
  Debug(this, "Worker %llu destroyed", thread_id_);
386
380
}
387
388
219
void Worker::New(const FunctionCallbackInfo<Value>& args) {
389
219
  Environment* env = Environment::GetCurrent(args);
390
391
219
  CHECK(args.IsConstructCall());
392
393
219
  if (env->isolate_data()->platform() == nullptr) {
394
    THROW_ERR_MISSING_PLATFORM_FOR_WORKER(env);
395
    return;
396
  }
397
398
219
  std::string url;
399
435
  std::shared_ptr<PerIsolateOptions> per_isolate_opts = nullptr;
400
401
435
  std::vector<std::string> exec_argv_out;
402
219
  bool has_explicit_exec_argv = false;
403
404
219
  CHECK_EQ(args.Length(), 2);
405
  // Argument might be a string or URL
406
657
  if (!args[0]->IsNullOrUndefined()) {
407
    Utf8Value value(
408
        args.GetIsolate(),
409
352
        args[0]->ToString(env->context()).FromMaybe(Local<String>()));
410
88
    url.append(value.out(), value.length());
411
  }
412
413
438
  if (args[1]->IsArray()) {
414
24
    Local<Array> array = args[1].As<Array>();
415
    // The first argument is reserved for program name, but we don't need it
416
    // in workers.
417
12
    has_explicit_exec_argv = true;
418
12
    std::vector<std::string> exec_argv = {""};
419
12
    uint32_t length = array->Length();
420
58
    for (uint32_t i = 0; i < length; i++) {
421
      Local<Value> arg;
422
51
      if (!array->Get(env->context(), i).ToLocal(&arg)) {
423
        return;
424
      }
425
      MaybeLocal<String> arg_v8_string =
426
34
          arg->ToString(env->context());
427
17
      if (arg_v8_string.IsEmpty()) {
428
        return;
429
      }
430
      Utf8Value arg_utf8_value(
431
          args.GetIsolate(),
432
17
          arg_v8_string.FromMaybe(Local<String>()));
433
34
      std::string arg_string(arg_utf8_value.out(), arg_utf8_value.length());
434
17
      exec_argv.push_back(arg_string);
435
17
    }
436
437
21
    std::vector<std::string> invalid_args{};
438
21
    std::vector<std::string> errors{};
439
12
    per_isolate_opts.reset(new PerIsolateOptions());
440
441
    // Using invalid_args as the v8_args argument as it stores unknown
442
    // options for the per isolate parser.
443
    options_parser::Parse(
444
        &exec_argv,
445
        &exec_argv_out,
446
        &invalid_args,
447
        per_isolate_opts.get(),
448
        kDisallowedInEnvironment,
449
12
        &errors);
450
451
    // The first argument is program name.
452
12
    invalid_args.erase(invalid_args.begin());
453

12
    if (errors.size() > 0 || invalid_args.size() > 0) {
454
      Local<Value> error;
455
6
      if (!ToV8Value(env->context(),
456
3
                     errors.size() > 0 ? errors : invalid_args)
457
9
                         .ToLocal(&error)) {
458
        return;
459
      }
460
      Local<String> key =
461
3
          FIXED_ONE_BYTE_STRING(env->isolate(), "invalidExecArgv");
462
      // Ignore the return value of Set() because exceptions bubble up to JS
463
      // when we return anyway.
464
9
      USE(args.This()->Set(env->context(), key, error));
465
3
      return;
466
9
    }
467
  }
468
216
  if (!has_explicit_exec_argv)
469
207
    exec_argv_out = env->exec_argv();
470
432
  new Worker(env, args.This(), url, per_isolate_opts, std::move(exec_argv_out));
471
}
472
473
214
void Worker::CloneParentEnvVars(const FunctionCallbackInfo<Value>& args) {
474
  Worker* w;
475
428
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
476
214
  CHECK(w->thread_joined_);  // The Worker has not started yet.
477
478
428
  w->env_vars_ = w->env()->env_vars()->Clone(args.GetIsolate());
479
}
480
481
1
void Worker::SetEnvVars(const FunctionCallbackInfo<Value>& args) {
482
  Worker* w;
483
2
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
484
1
  CHECK(w->thread_joined_);  // The Worker has not started yet.
485
486
2
  CHECK(args[0]->IsObject());
487
1
  w->env_vars_ = KVStore::CreateMapKVStore();
488
1
  w->env_vars_->AssignFromObject(args.GetIsolate()->GetCurrentContext(),
489
3
                                args[0].As<Object>());
490
}
491
492
215
void Worker::StartThread(const FunctionCallbackInfo<Value>& args) {
493
  Worker* w;
494
430
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
495
215
  Mutex::ScopedLock lock(w->mutex_);
496
497
  // The object now owns the created thread and should not be garbage collected
498
  // until that finishes.
499
215
  w->ClearWeak();
500
501
215
  w->env()->add_sub_worker_context(w);
502
215
  w->stopped_ = false;
503
215
  w->thread_joined_ = false;
504
505
597
  w->on_thread_finished_.Install(w->env(), w, [](uv_async_t* handle) {
506
191
    Worker* w_ = static_cast<Worker*>(handle->data);
507
191
    CHECK(w_->is_stopped());
508
191
    w_->parent_port_ = nullptr;
509
191
    w_->JoinThread();
510
187
    delete w_;
511
808
  });
512
513
  uv_thread_options_t thread_options;
514
215
  thread_options.flags = UV_THREAD_HAS_STACK_SIZE;
515
215
  thread_options.stack_size = kStackSize;
516
860
  CHECK_EQ(uv_thread_create_ex(&w->tid_, &thread_options, [](void* arg) {
517
    Worker* w = static_cast<Worker*>(arg);
518
    const uintptr_t stack_top = reinterpret_cast<uintptr_t>(&arg);
519
520
    // Leave a few kilobytes just to make sure we're within limits and have
521
    // some space to do work in C++ land.
522
    w->stack_base_ = stack_top - (kStackSize - kStackBufferSize);
523
524
    w->Run();
525
526
    Mutex::ScopedLock lock(w->mutex_);
527
    w->on_thread_finished_.Stop();
528
215
  }, static_cast<void*>(w)), 0);
529
}
530
531
29
void Worker::StopThread(const FunctionCallbackInfo<Value>& args) {
532
  Worker* w;
533
58
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
534
535
29
  Debug(w, "Worker %llu is getting stopped by parent", w->thread_id_);
536
29
  w->Exit(1);
537
}
538
539
31
void Worker::Ref(const FunctionCallbackInfo<Value>& args) {
540
  Worker* w;
541
62
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
542
31
  uv_ref(reinterpret_cast<uv_handle_t*>(w->on_thread_finished_.GetHandle()));
543
}
544
545
5
void Worker::Unref(const FunctionCallbackInfo<Value>& args) {
546
  Worker* w;
547
10
  ASSIGN_OR_RETURN_UNWRAP(&w, args.This());
548
5
  uv_unref(reinterpret_cast<uv_handle_t*>(w->on_thread_finished_.GetHandle()));
549
}
550
551
80
void Worker::Exit(int code) {
552
80
  Mutex::ScopedLock lock(mutex_);
553
80
  Debug(this, "Worker %llu called Exit(%d)", thread_id_, code);
554
80
  if (env_ != nullptr) {
555
69
    exit_code_ = code;
556
69
    Stop(env_);
557
  } else {
558
11
    stopped_ = true;
559
80
  }
560
80
}
561
562
namespace {
563
564
// Return the MessagePort that is global for this Environment and communicates
565
// with the internal [kPort] port of the JS Worker class in the parent thread.
566
406
void GetEnvMessagePort(const FunctionCallbackInfo<Value>& args) {
567
406
  Environment* env = Environment::GetCurrent(args);
568
406
  Local<Object> port = env->message_port();
569
406
  if (!port.IsEmpty()) {
570
1218
    CHECK_EQ(port->CreationContext()->GetIsolate(), args.GetIsolate());
571
812
    args.GetReturnValue().Set(port);
572
  }
573
406
}
574
575
3580
void InitWorker(Local<Object> target,
576
                Local<Value> unused,
577
                Local<Context> context,
578
                void* priv) {
579
3580
  Environment* env = Environment::GetCurrent(context);
580
581
  {
582
3580
    Local<FunctionTemplate> w = env->NewFunctionTemplate(Worker::New);
583
584
7160
    w->InstanceTemplate()->SetInternalFieldCount(1);
585
7160
    w->Inherit(AsyncWrap::GetConstructorTemplate(env));
586
587
3580
    env->SetProtoMethod(w, "setEnvVars", Worker::SetEnvVars);
588
3580
    env->SetProtoMethod(w, "cloneParentEnvVars", Worker::CloneParentEnvVars);
589
3580
    env->SetProtoMethod(w, "startThread", Worker::StartThread);
590
3580
    env->SetProtoMethod(w, "stopThread", Worker::StopThread);
591
3580
    env->SetProtoMethod(w, "ref", Worker::Ref);
592
3580
    env->SetProtoMethod(w, "unref", Worker::Unref);
593
594
    Local<String> workerString =
595
3580
        FIXED_ONE_BYTE_STRING(env->isolate(), "Worker");
596
3580
    w->SetClassName(workerString);
597
    target->Set(env->context(),
598
                workerString,
599
17900
                w->GetFunction(env->context()).ToLocalChecked()).Check();
600
  }
601
602
3580
  env->SetMethod(target, "getEnvMessagePort", GetEnvMessagePort);
603
604
  target
605
      ->Set(env->context(),
606
            env->thread_id_string(),
607
17900
            Number::New(env->isolate(), static_cast<double>(env->thread_id())))
608
7160
      .Check();
609
610
  target
611
      ->Set(env->context(),
612
            FIXED_ONE_BYTE_STRING(env->isolate(), "isMainThread"),
613
17900
            Boolean::New(env->isolate(), env->is_main_thread()))
614
7160
      .Check();
615
616
  target
617
      ->Set(env->context(),
618
            FIXED_ONE_BYTE_STRING(env->isolate(), "ownsProcessState"),
619
17900
            Boolean::New(env->isolate(), env->owns_process_state()))
620
7160
      .Check();
621
3580
}
622
623
}  // anonymous namespace
624
625
}  // namespace worker
626
}  // namespace node
627
628
5033
NODE_MODULE_CONTEXT_AWARE_INTERNAL(worker, node::worker::InitWorker)