{"action":"created","comment":{"url":"https://api.github.com/repos/iojs/io.js/pulls/comments/30347984","id":30347984,"diff_hunk":"@@ -0,0 +1,694 @@\n+#include \"worker.h\"\n+\n+#include \"node.h\"\n+#include \"node_internals.h\"\n+#include \"uv.h\"\n+#include \"env.h\"\n+#include \"env-inl.h\"\n+#include \"v8.h\"\n+#include \"libplatform/libplatform.h\"\n+#include \"v8-debug.h\"\n+#include \"util.h\"\n+#include \"util-inl.h\"\n+#include \"producer-consumer-queue.h\"\n+\n+#include <new>\n+\n+namespace node {\n+\n+using v8::Handle;\n+using v8::Object;\n+using v8::FunctionCallbackInfo;\n+using v8::Value;\n+using v8::String;\n+using v8::FunctionTemplate;\n+using v8::Local;\n+using v8::Context;\n+using v8::HandleScope;\n+using v8::SealHandleScope;\n+using v8::Integer;\n+using v8::Isolate;\n+using v8::Function;\n+using v8::Persistent;\n+using v8::Locker;\n+using v8::TryCatch;\n+\n+WorkerContext::WorkerContext(Environment* owner_env,\n+                             Handle<Object> owner_wrapper,\n+                             uv_loop_t* event_loop,\n+                             int argc,\n+                             const char** argv,\n+                             int exec_argc,\n+                             const char** exec_argv,\n+                             bool keep_alive)\n+    : owner_env_(owner_env),\n+      worker_isolate_(Isolate::New()),\n+      owner_wrapper_(owner_env->isolate(), owner_wrapper),\n+      keep_alive_(keep_alive),\n+      event_loop_(event_loop),\n+      owner_notifications_(owner_event_loop(),\n+                           OwnerNotificationCallback,\n+                           this),\n+      worker_notifications_(worker_event_loop(),\n+                            WorkerNotificationCallback,\n+                            this),\n+      argc_(argc),\n+      argv_(argv),\n+      exec_argc_(exec_argc),\n+      exec_argv_(exec_argv) {\n+  CHECK_NE(event_loop, nullptr);\n+  CHECK_NE(event_loop, uv_default_loop());\n+  CHECK_CALLED_FROM_OWNER(this);\n+  node::Wrap(owner_wrapper, this);\n+\n+  owner_env->AddSubWorkerContext(this);\n+\n+  CHECK_EQ(uv_mutex_init(termination_mutex()), 0);\n+  CHECK_EQ(uv_mutex_init(api_mutex()), 0);\n+  CHECK_EQ(uv_mutex_init(initialization_mutex()), 0);\n+  CHECK_EQ(uv_mutex_init(to_owner_messages_mutex()), 0);\n+  CHECK_EQ(uv_mutex_init(to_worker_messages_mutex()), 0);\n+}\n+\n+void WorkerContext::New(const FunctionCallbackInfo<Value>& args) {\n+  Environment* env = Environment::GetCurrent(args);\n+  HandleScope scope(env->isolate());\n+  CHECK(args[0]->IsString());\n+  CHECK(args[1]->IsObject());\n+  Local<String> entry_module_path = args[0].As<String>();\n+  bool keep_alive =\n+      args[1].As<Object>()->Get(env->keep_alive_string())->BooleanValue();\n+\n+  uv_loop_t* worker_event_loop = static_cast<uv_loop_t*>(\n+      malloc(sizeof(uv_loop_t)));\n+  int err = uv_loop_init(worker_event_loop);\n+  if (err != 0) {\n+    free(worker_event_loop);\n+    return env->ThrowUVException(err, \"uv_loop_init\");\n+  }\n+\n+  // Heap allocated cause this stack frame can be gone by the time the worker\n+  // needs them.\n+  int argc = 2;\n+  char** argv = new char*[argc];\n+  // TODO(petkaantonov) should use correct executable name here\n+  argv[0] = const_cast<char*>(\"node\");\n+  argv[1] = ToUtf8Value(env->isolate(), entry_module_path);\n+  // TODO(petkaantonov) should use the process's real exec args\n+  int exec_argc = 0;\n+  char** exec_argv = new char*[exec_argc];\n+\n+  WorkerContext* context =\n+      new WorkerContext(env,\n+                        args.This(),\n+                        worker_event_loop,\n+                        argc,\n+                        const_cast<const char**>(argv),\n+                        exec_argc,\n+                        const_cast<const char**>(exec_argv),\n+                        keep_alive);\n+\n+  err = uv_thread_create(context->worker_thread(), RunWorkerInstance, context);\n+\n+  if (err != 0) {\n+    context->worker_notifications()->Dispose();\n+    context->Dispose();\n+    CHECK_EQ(uv_loop_close(worker_event_loop), 0);\n+    free(worker_event_loop);\n+    return env->ThrowUVException(err, \"uv_thread_create\");\n+  }\n+}\n+\n+void WorkerContext::PostMessageToOwner(WorkerMessage* message) {\n+  if (termination_kind() != NONE) {\n+    delete message;\n+    return;\n+  }\n+\n+  if (!to_owner_messages_primary_.PushBack(message)) {\n+    ScopedLock::Mutex lock(to_owner_messages_mutex());\n+    to_owner_messages_backup_.PushBack(message);","path":"src/worker.cc","position":130,"original_position":130,"commit_id":"fab6db9b29bd8d4bafe518e998f57cff4fb438af","original_commit_id":"fab6db9b29bd8d4bafe518e998f57cff4fb438af","user":{"login":"trevnorris","id":289174,"avatar_url":"https://avatars.githubusercontent.com/u/289174?v=3","gravatar_id":"","url":"https://api.github.com/users/trevnorris","html_url":"https://github.com/trevnorris","followers_url":"https://api.github.com/users/trevnorris/followers","following_url":"https://api.github.com/users/trevnorris/following{/other_user}","gists_url":"https://api.github.com/users/trevnorris/gists{/gist_id}","starred_url":"https://api.github.com/users/trevnorris/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/trevnorris/subscriptions","organizations_url":"https://api.github.com/users/trevnorris/orgs","repos_url":"https://api.github.com/users/trevnorris/repos","events_url":"https://api.github.com/users/trevnorris/events{/privacy}","received_events_url":"https://api.github.com/users/trevnorris/received_events","type":"User","site_admin":false},"body":"would it be faster if you used a lock-less single producer single consumer queue to communicate from spawned thread to main thread, then linearly walk all queues to see what's been added? this way there's never any lock contention, and I don't imagine there will be that many threads spawned.","created_at":"2015-05-14T18:27:55Z","updated_at":"2015-05-14T18:27:55Z","html_url":"https://github.com/iojs/io.js/pull/1159#discussion_r30347984","pull_request_url":"https://api.github.com/repos/iojs/io.js/pulls/1159","_links":{"self":{"href":"https://api.github.com/repos/iojs/io.js/pulls/comments/30347984"},"html":{"href":"https://github.com/iojs/io.js/pull/1159#discussion_r30347984"},"pull_request":{"href":"https://api.github.com/repos/iojs/io.js/pulls/1159"}}},"pull_request":{"url":"https://api.github.com/repos/iojs/io.js/pulls/1159","id":31208867,"html_url":"https://github.com/iojs/io.js/pull/1159","diff_url":"https://github.com/iojs/io.js/pull/1159.diff","patch_url":"https://github.com/iojs/io.js/pull/1159.patch","issue_url":"https://api.github.com/repos/iojs/io.js/issues/1159","number":1159,"state":"open","locked":false,"title":"workers: initial implementation","user":{"login":"petkaantonov","id":829734,"avatar_url":"https://avatars.githubusercontent.com/u/829734?v=3","gravatar_id":"","url":"https://api.github.com/users/petkaantonov","html_url":"https://github.com/petkaantonov","followers_url":"https://api.github.com/users/petkaantonov/followers","following_url":"https://api.github.com/users/petkaantonov/following{/other_user}","gists_url":"https://api.github.com/users/petkaantonov/gists{/gist_id}","starred_url":"https://api.github.com/users/petkaantonov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petkaantonov/subscriptions","organizations_url":"https://api.github.com/users/petkaantonov/orgs","repos_url":"https://api.github.com/users/petkaantonov/repos","events_url":"https://api.github.com/users/petkaantonov/events{/privacy}","received_events_url":"https://api.github.com/users/petkaantonov/received_events","type":"User","site_admin":false},"body":"Workers are light-weight processes that are backed by OS threads.\r\n\r\nThis is an initial implementation that is behind the flag `--experimental-workers`. It should have very little effect on other io.js components, even if the flag is enabled. The module can be required by doing  `var Worker = require('worker');` - see how the tests do it in test/workers folder.\r\n\r\n<hr>\r\n\r\nWhat is currently implemented:\r\n\r\nWorker constructor which takes an entry module and an optional options object. Currently (I have some ideas for more) the only option is `keepAlive`:\r\n\r\n```js\r\nvar worker = new Worker('worker.js', {keepAlive: false})\r\n```\r\n\r\nThe option defaults to `true`, which means the worker thread will stay alive even when its event loop is completely empty. This is because you might not have\r\nanything to send to the worker right away when a server is started for instance.\r\n\r\nA worker object is an event emitter, with the events `'message'`, `'exit'`, and `'error'` and public methods `postMessage`, `terminate`, `ref` and `unref`:\r\n\r\n - `'message'` event will receive currently a single argument, that is directly the message posted from the worker thread.\r\n - `'exit'` event is emitted when the worker thread exits and receives the exit code as argument.\r\n - `'error'` event is emitted when there is an uncaught exception in the worker which would abort a normal node process. The argument is the error thrown,\r\n   if it's an error object, its builtin type, stack, name and message are retained in the copy. It will additionally contain extra properties that were in the\r\n   original error, such as `.code`.\r\n\r\n - `postMessage([message])` where message is structured clonable* can be used to send a message to the worker thread. Additionally internal messaging support is implemented\r\n    that doesn't need to copy data or go through JS. (* currently implemented as JSON to keep PR small, more details later)\r\n\r\n - `terminate([callback])` terminates the worker by using V8 TerminateExecution and uv_async_send, so the only thing that\r\n    can defer a worker's termination is when it is executing C code which shouldn't contain any infinite loop or such :-).\r\n\r\n - `unref` and `ref` work like timer's respective methods, however keep in mind that a worker will terminate if its owner terminates.\r\n\r\nInside a worker thread, several process-wide features are disabled for example setting the process' current working directory, title, environment variables or umask. Workers can still read these, but only the main thread can change them. Otherwise all core modules and javascript works normally inside a worker, including `console.log`.\r\n\r\nInside worker thread, the `Worker` constructor object is an event emitter, with the event `'message'` and the method `postMessage()` to communicate with its owner (see tests for usage). The worker constructor can be used to construct nested workers with the worker being their owner. Passing messages from grand child to grand parent requires the child's parent to pass its message through, however transferable message ports can be implemented if use cases emerge.\r\n\r\nNested workers were implemented because Web Workers support them too (except Chrome) and I envision that a setup with ${CPU cores} amount of web servers with N amounts of sub-workers for each will be very useful.\r\n\r\n2 new public process read-only properties are introduced `process.isMainInstance` and `process.isWorkerInstance`.\r\n\r\n\r\n<hr>\r\n\r\nAdvantages of workers over processes:\r\n\r\n - Internal ITC (not passing JS objects) is ridiculously fast with the wait-free message channel\r\n - User ITC is probably faster as well, not having the overhead of JSON parsing and serialiazing\r\n   - And can also transfer more kinds of objects, such as Dates, Maps, Sets, objects with circular references...\r\n   - Additionally ownership transfer of external resources like ArrayBuffers and external strings can be done without copying\r\n   - Ownership transfer of uv handles will probably require support from uv so that handles can be detached from an event loop\r\n    and adopted by another.\r\n - Threads have less context-switch overhead, making it feasible to run many more workers than there are CPU cores\r\n  which is more graceful in high load situations. And if we implement worker-cluster, there shouldn't be the need\r\n  to use home-made round-robing scheduler.\r\n - Free of problems like process.send not always being synchronous\r\n - There is just one process to kill and no runaway processes\r\n\r\nDisadvantages:\r\n\r\n - Cannot rely on OS to free resources upon worker exit. However the architecture that\r\n  enables freeing all resources upon Environment destruction is there and\r\n  is already used to free all resources that a new worker that doesn't do\r\n  anything but exit immediately allocates.\r\n\r\n - C code maintainers need to think about thread-safety. However, there is generally\r\n  little amount of shared resources as most stuff is tied to a v8 isolate or a uv event loop\r\n  which are exclusive to a worker.\r\n\r\nCompared to traditional threading:\r\n\r\n - Workers are expensive to create, one does not simply spin up a worker to do 1 task and throw it away.\r\n - Workers cannot share any JS memory (this is impossible in V8), to JS users they appear pretty much as isolated as real processes\r\n\r\n<hr>\r\n\r\nTo keep the PR size small (believe it or not, deep copying will take much more code than workers alone), objects are still copied by JSON serialization. I want to implement something like the [structured clone](http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data) with some differences as the spec has some seriously nasty parts that are not really needed in practice but force the implementation to be so slow as to defeat the whole point of using it in the first place.\r\n\r\nSo what cannot be implemented from the algorithm:\r\n\r\n - The html5 objects like File (since we don't have those)\r\n - Named properties of Arrays (inconsistent and extremely nasty to implement using v8's api)\r\n - (implicit) If you have sparse array and define indexed properties in protototypes, then those values in prototypes are used in the resulting array. V8 has internal check that array and object prototypes are clean of indexed properties, but we don't have access to that.\r\n\r\nWhat could be implemented but don't want to implement from the algorithm:\r\n\r\n - Normal properties of Map and Set. This would be inconsistent and add more stuff to implement, but certainly doable.\r\n\r\nWhat I mean by inconsistent is that copying map, set and array properties is inconsistent because the properties of Date, RegExp, Boolean, Number, and String objects are dropped.\r\n\r\n<hr>\r\n\r\nTo make reviewing this easier, a description of changes in each file follows:\r\n\r\n - **test/workers/\\***, **tools/test.py**, **Makefile**\r\n    - For testing. Run `make test-workers`. Worker tests are not ran as a part of `make test`\r\n - **worker.cc**, **worker.h**, **worker.js**\r\n    - Majority of the worker implementation is here\r\n - **util.h**, **util-inl.h**, **util.cc**\r\n    - Added `STATIC_ASSERT`\r\n    - Added some threading helpers\r\n    - Added a hack to get a `char*` from js string (TODO)\r\n    - Added `Remove` method to `ListHead`\r\n - **smalloc.cc**, **smalloc.h**\r\n    - Extracted `CallbackInfo` class declaration to a header\r\n    - Moved `ALLOC_ID` to `env.h`\r\n - **producer-consumer-queue.h**\r\n    - Modified folly's wait-free single-producer single-consumer queue.\r\n - **persistent-handle-cleanup.cc**, **persistent-handle-cleanup.h**.\r\n    - Because finalizers are not guaranteed to be called even when a v8 isolate is disposed, these implement `PersistentHandleVisitor` that is used to walk all unfinalized weak persistent handles to release their C resources upon `Environment` destruction\r\n - **notification-channel.cc**, **notification-channel.h**\r\n    - These work around `uv_async_send` unreliability when async handles need to be unreffed\r\n - **node-internals.h**\r\n    - Removed NodeInstanceData - the event loop code for worker became too different from the main thread event loop code for this to be useful\r\n - **node-contextify.cc**\r\n    - Extracted out `ContextifyScript` class declaration to a header. The diff looks horrible but there is no real changes\r\n    - Added check to prevent cancellation of termination in `EvalMachine` method\r\n - **node.js**\r\n    - Added worker mode startup and cleanup registration for stdio and signal handles. (`environment->CleanupHandles()` is actually unused by main instance, but workers use it)\r\n - **node.cc**\r\n    - Added locking around process-wide methods. (If uv's rwlock still causes deadlocks in Windows 2003, it should be changed to mutex).\r\n    - Added worker-only locking for module initialization, the `ApiLock()` is null for main instance but workers need it because module initialization isn't termination safe.\r\n    - Made `MakeCallback` safe to call inside workers that are being abruptly terminated\r\n    - Changed all `exit` calls to `env->Exit()` (which only exits the process if called from main thread)\r\n    - Added an export for `process._registerHandleCleanup()`\r\n    - Moved `atexit` from `LoadEnvironment` to `Init` (probably not the right place, but it cannot be in `LoadEnvironment`)\r\n    - Added `CreateEnvironment` overload to be called from worker threads\r\n    - Extracted Environment initialization to `InitializeEnvironment`\r\n    - Removed all worker checks from StartNodeInstance and renamed it to RunMainInstance. Added a call to `TerminateSubWorkers()` before exit event is emitted.\r\n    - Added `ShutdownPlatform` and `delete platform` at the end of `Start` method.\r\n - **handle_wrap.cc**, **handle_wrap.h**\r\n    - Added a way to register handle cleanups upon Environment destruction.\r\n - **env.h**, **env-inl.h**\r\n    - Introduced a `ClassId` enum which weak persistent wrappers can use so that they can be walked upon Environment destruction and cleaned up for sure.\r\n    - Added some worker related methods\r\n    - Added `set_using_cares()`, which means that in Environment destructor `destroy_cares` is called\r\n    - Added `CanCallIntoJs()` method: main instance can always call into js but workers can be in the middle of termination in which case one cannot call into js anymore\r\n    - Added `Exit` method which does process exit if called from main thread and worker termination if called from worker thread\r\n - **cares_wrap.cc**\r\n    - Added `InitAresOnce` to do one-time process-wide initialization\r\n    - Added a call to `env->set_using_cares()` (see env.h above)\r\n - **async-wrap.cc**\r\n    - Made same changes to `MakeCallback` as were made to `node::MakeCallback`\r\n - **timers.js**\r\n    - Added handle cleanup registrations for timer handles so that when a worker `Environment` is destructed, the handles won't leak\r\n - **child_process.js**\r\n    - Added handle cleanup registration for the pipe handle so that when a worker `Environment` is destructed, the handle won't leak. The other handles probably leak too but this was the only one that is caught by current tests. There is currently an assertion in worker termination code for leaked handles.\r\n - **LICENSE**\r\n    - Added ProducerConsumerQueue's license\r\n","created_at":"2015-03-15T22:38:28Z","updated_at":"2015-05-14T18:27:55Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3c98427433a92b89b4382a76ab092ed75e9b5c25","assignee":{"login":"petkaantonov","id":829734,"avatar_url":"https://avatars.githubusercontent.com/u/829734?v=3","gravatar_id":"","url":"https://api.github.com/users/petkaantonov","html_url":"https://github.com/petkaantonov","followers_url":"https://api.github.com/users/petkaantonov/followers","following_url":"https://api.github.com/users/petkaantonov/following{/other_user}","gists_url":"https://api.github.com/users/petkaantonov/gists{/gist_id}","starred_url":"https://api.github.com/users/petkaantonov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petkaantonov/subscriptions","organizations_url":"https://api.github.com/users/petkaantonov/orgs","repos_url":"https://api.github.com/users/petkaantonov/repos","events_url":"https://api.github.com/users/petkaantonov/events{/privacy}","received_events_url":"https://api.github.com/users/petkaantonov/received_events","type":"User","site_admin":false},"milestone":null,"commits_url":"https://api.github.com/repos/iojs/io.js/pulls/1159/commits","review_comments_url":"https://api.github.com/repos/iojs/io.js/pulls/1159/comments","review_comment_url":"https://api.github.com/repos/iojs/io.js/pulls/comments{/number}","comments_url":"https://api.github.com/repos/iojs/io.js/issues/1159/comments","statuses_url":"https://api.github.com/repos/iojs/io.js/statuses/fab6db9b29bd8d4bafe518e998f57cff4fb438af","head":{"label":"petkaantonov:workers-implementation","ref":"workers-implementation","sha":"fab6db9b29bd8d4bafe518e998f57cff4fb438af","user":{"login":"petkaantonov","id":829734,"avatar_url":"https://avatars.githubusercontent.com/u/829734?v=3","gravatar_id":"","url":"https://api.github.com/users/petkaantonov","html_url":"https://github.com/petkaantonov","followers_url":"https://api.github.com/users/petkaantonov/followers","following_url":"https://api.github.com/users/petkaantonov/following{/other_user}","gists_url":"https://api.github.com/users/petkaantonov/gists{/gist_id}","starred_url":"https://api.github.com/users/petkaantonov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petkaantonov/subscriptions","organizations_url":"https://api.github.com/users/petkaantonov/orgs","repos_url":"https://api.github.com/users/petkaantonov/repos","events_url":"https://api.github.com/users/petkaantonov/events{/privacy}","received_events_url":"https://api.github.com/users/petkaantonov/received_events","type":"User","site_admin":false},"repo":{"id":30495325,"name":"io.js","full_name":"petkaantonov/io.js","owner":{"login":"petkaantonov","id":829734,"avatar_url":"https://avatars.githubusercontent.com/u/829734?v=3","gravatar_id":"","url":"https://api.github.com/users/petkaantonov","html_url":"https://github.com/petkaantonov","followers_url":"https://api.github.com/users/petkaantonov/followers","following_url":"https://api.github.com/users/petkaantonov/following{/other_user}","gists_url":"https://api.github.com/users/petkaantonov/gists{/gist_id}","starred_url":"https://api.github.com/users/petkaantonov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petkaantonov/subscriptions","organizations_url":"https://api.github.com/users/petkaantonov/orgs","repos_url":"https://api.github.com/users/petkaantonov/repos","events_url":"https://api.github.com/users/petkaantonov/events{/privacy}","received_events_url":"https://api.github.com/users/petkaantonov/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/petkaantonov/io.js","description":"A friendly fork of Node.js with an open governance model","fork":true,"url":"https://api.github.com/repos/petkaantonov/io.js","forks_url":"https://api.github.com/repos/petkaantonov/io.js/forks","keys_url":"https://api.github.com/repos/petkaantonov/io.js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/petkaantonov/io.js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/petkaantonov/io.js/teams","hooks_url":"https://api.github.com/repos/petkaantonov/io.js/hooks","issue_events_url":"https://api.github.com/repos/petkaantonov/io.js/issues/events{/number}","events_url":"https://api.github.com/repos/petkaantonov/io.js/events","assignees_url":"https://api.github.com/repos/petkaantonov/io.js/assignees{/user}","branches_url":"https://api.github.com/repos/petkaantonov/io.js/branches{/branch}","tags_url":"https://api.github.com/repos/petkaantonov/io.js/tags","blobs_url":"https://api.github.com/repos/petkaantonov/io.js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/petkaantonov/io.js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/petkaantonov/io.js/git/refs{/sha}","trees_url":"https://api.github.com/repos/petkaantonov/io.js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/petkaantonov/io.js/statuses/{sha}","languages_url":"https://api.github.com/repos/petkaantonov/io.js/languages","stargazers_url":"https://api.github.com/repos/petkaantonov/io.js/stargazers","contributors_url":"https://api.github.com/repos/petkaantonov/io.js/contributors","subscribers_url":"https://api.github.com/repos/petkaantonov/io.js/subscribers","subscription_url":"https://api.github.com/repos/petkaantonov/io.js/subscription","commits_url":"https://api.github.com/repos/petkaantonov/io.js/commits{/sha}","git_commits_url":"https://api.github.com/repos/petkaantonov/io.js/git/commits{/sha}","comments_url":"https://api.github.com/repos/petkaantonov/io.js/comments{/number}","issue_comment_url":"https://api.github.com/repos/petkaantonov/io.js/issues/comments{/number}","contents_url":"https://api.github.com/repos/petkaantonov/io.js/contents/{+path}","compare_url":"https://api.github.com/repos/petkaantonov/io.js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/petkaantonov/io.js/merges","archive_url":"https://api.github.com/repos/petkaantonov/io.js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/petkaantonov/io.js/downloads","issues_url":"https://api.github.com/repos/petkaantonov/io.js/issues{/number}","pulls_url":"https://api.github.com/repos/petkaantonov/io.js/pulls{/number}","milestones_url":"https://api.github.com/repos/petkaantonov/io.js/milestones{/number}","notifications_url":"https://api.github.com/repos/petkaantonov/io.js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/petkaantonov/io.js/labels{/name}","releases_url":"https://api.github.com/repos/petkaantonov/io.js/releases{/id}","created_at":"2015-02-08T15:30:16Z","updated_at":"2015-02-08T15:30:19Z","pushed_at":"2015-05-11T13:00:11Z","git_url":"git://github.com/petkaantonov/io.js.git","ssh_url":"git@github.com:petkaantonov/io.js.git","clone_url":"https://github.com/petkaantonov/io.js.git","svn_url":"https://github.com/petkaantonov/io.js","homepage":"https://iojs.org","size":129874,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"v1.x"}},"base":{"label":"iojs:v1.x","ref":"v1.x","sha":"ca8c9ec2c8ceab7c900167de6901f0acb9a13248","user":{"login":"iojs","id":9950313,"avatar_url":"https://avatars.githubusercontent.com/u/9950313?v=3","gravatar_id":"","url":"https://api.github.com/users/iojs","html_url":"https://github.com/iojs","followers_url":"https://api.github.com/users/iojs/followers","following_url":"https://api.github.com/users/iojs/following{/other_user}","gists_url":"https://api.github.com/users/iojs/gists{/gist_id}","starred_url":"https://api.github.com/users/iojs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iojs/subscriptions","organizations_url":"https://api.github.com/users/iojs/orgs","repos_url":"https://api.github.com/users/iojs/repos","events_url":"https://api.github.com/users/iojs/events{/privacy}","received_events_url":"https://api.github.com/users/iojs/received_events","type":"Organization","site_admin":false},"repo":{"id":27193779,"name":"io.js","full_name":"iojs/io.js","owner":{"login":"iojs","id":9950313,"avatar_url":"https://avatars.githubusercontent.com/u/9950313?v=3","gravatar_id":"","url":"https://api.github.com/users/iojs","html_url":"https://github.com/iojs","followers_url":"https://api.github.com/users/iojs/followers","following_url":"https://api.github.com/users/iojs/following{/other_user}","gists_url":"https://api.github.com/users/iojs/gists{/gist_id}","starred_url":"https://api.github.com/users/iojs/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iojs/subscriptions","organizations_url":"https://api.github.com/users/iojs/orgs","repos_url":"https://api.github.com/users/iojs/repos","events_url":"https://api.github.com/users/iojs/events{/privacy}","received_events_url":"https://api.github.com/users/iojs/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/iojs/io.js","description":"A friendly fork of Node.js with an open governance model","fork":false,"url":"https://api.github.com/repos/iojs/io.js","forks_url":"https://api.github.com/repos/iojs/io.js/forks","keys_url":"https://api.github.com/repos/iojs/io.js/keys{/key_id}","collaborators_url":"https://api.github.com/repos/iojs/io.js/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/iojs/io.js/teams","hooks_url":"https://api.github.com/repos/iojs/io.js/hooks","issue_events_url":"https://api.github.com/repos/iojs/io.js/issues/events{/number}","events_url":"https://api.github.com/repos/iojs/io.js/events","assignees_url":"https://api.github.com/repos/iojs/io.js/assignees{/user}","branches_url":"https://api.github.com/repos/iojs/io.js/branches{/branch}","tags_url":"https://api.github.com/repos/iojs/io.js/tags","blobs_url":"https://api.github.com/repos/iojs/io.js/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/iojs/io.js/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/iojs/io.js/git/refs{/sha}","trees_url":"https://api.github.com/repos/iojs/io.js/git/trees{/sha}","statuses_url":"https://api.github.com/repos/iojs/io.js/statuses/{sha}","languages_url":"https://api.github.com/repos/iojs/io.js/languages","stargazers_url":"https://api.github.com/repos/iojs/io.js/stargazers","contributors_url":"https://api.github.com/repos/iojs/io.js/contributors","subscribers_url":"https://api.github.com/repos/iojs/io.js/subscribers","subscription_url":"https://api.github.com/repos/iojs/io.js/subscription","commits_url":"https://api.github.com/repos/iojs/io.js/commits{/sha}","git_commits_url":"https://api.github.com/repos/iojs/io.js/git/commits{/sha}","comments_url":"https://api.github.com/repos/iojs/io.js/comments{/number}","issue_comment_url":"https://api.github.com/repos/iojs/io.js/issues/comments{/number}","contents_url":"https://api.github.com/repos/iojs/io.js/contents/{+path}","compare_url":"https://api.github.com/repos/iojs/io.js/compare/{base}...{head}","merges_url":"https://api.github.com/repos/iojs/io.js/merges","archive_url":"https://api.github.com/repos/iojs/io.js/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/iojs/io.js/downloads","issues_url":"https://api.github.com/repos/iojs/io.js/issues{/number}","pulls_url":"https://api.github.com/repos/iojs/io.js/pulls{/number}","milestones_url":"https://api.github.com/repos/iojs/io.js/milestones{/number}","notifications_url":"https://api.github.com/repos/iojs/io.js/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/iojs/io.js/labels{/name}","releases_url":"https://api.github.com/repos/iojs/io.js/releases{/id}","created_at":"2014-11-26T19:57:11Z","updated_at":"2015-05-14T17:48:09Z","pushed_at":"2015-05-14T15:47:31Z","git_url":"git://github.com/iojs/io.js.git","ssh_url":"git@github.com:iojs/io.js.git","clone_url":"https://github.com/iojs/io.js.git","svn_url":"https://github.com/iojs/io.js","homepage":"https://iojs.org","size":179392,"stargazers_count":12313,"watchers_count":12313,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":901,"mirror_url":null,"open_issues_count":256,"forks":901,"open_issues":256,"watchers":12313,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/iojs/io.js/pulls/1159"},"html":{"href":"https://github.com/iojs/io.js/pull/1159"},"issue":{"href":"https://api.github.com/repos/iojs/io.js/issues/1159"},"comments":{"href":"https://api.github.com/repos/iojs/io.js/issues/1159/comments"},"review_comments":{"href":"https://api.github.com/repos/iojs/io.js/pulls/1159/comments"},"review_comment":{"href":"https://api.github.com/repos/iojs/io.js/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/iojs/io.js/pulls/1159/commits"},"statuses":{"href":"https://api.github.com/repos/iojs/io.js/statuses/fab6db9b29bd8d4bafe518e998f57cff4fb438af"}}}}