diff --git a/hphp/idl/asio.idl.json b/hphp/idl/asio.idl.json index a10792078..b2439e393 100644 --- a/hphp/idl/asio.idl.json +++ b/hphp/idl/asio.idl.json @@ -640,6 +640,68 @@ "consts": [ ] }, + { + "name": "GenVectorWaitHandle", + "parent": "BlockableWaitHandle", + "desc": "A wait handle representing a vector of asynchronous operations", + "flags": [ + "HasDocComment" + ], + "funcs": [ + { + "name": "__construct", + "flags": [ + "IsPrivate", + "HasDocComment" + ], + "return": { + "type": null + }, + "args": [ + ] + }, + { + "name": "create", + "desc": "Create a wait handle that waits for a given vector of dependencies", + "flags": [ + "IsStatic", + "HasDocComment" + ], + "return": { + "type": "Object", + "desc": "A WaitHandle that will wait for a given vector of dependencies and return their results" + }, + "args": [ + { + "name": "dependencies", + "type": "Variant", + "desc": "A Vector of dependencies to wait for" + } + ] + }, + { + "name": "setOnCreateCallback", + "desc": "Set callback for when a GenVectorWaitHandle is created", + "flags": [ + "IsStatic", + "HasDocComment", + "HipHopSpecific" + ], + "return": { + "type": null + }, + "args": [ + { + "name": "on_create_cb", + "type": "Variant", + "desc": "A Closure to be called on creation" + } + ] + } + ], + "consts": [ + ] + }, { "name": "SetResultToRefWaitHandle", "parent": "BlockableWaitHandle", diff --git a/hphp/runtime/base/server/libevent_server_factory.cpp b/hphp/runtime/base/server/libevent_server_factory.cpp index 9806c519c..6d0af6354 100644 --- a/hphp/runtime/base/server/libevent_server_factory.cpp +++ b/hphp/runtime/base/server/libevent_server_factory.cpp @@ -1,3 +1,18 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ // Copyright 2004-present Facebook. All Rights Reserved. #include "hphp/runtime/base/server/libevent_server.h" #include "hphp/runtime/base/server/libevent_server_with_fd.h" diff --git a/hphp/runtime/base/server/warmup_request_handler.cpp b/hphp/runtime/base/server/warmup_request_handler.cpp index 44bdd99a1..67e8f014d 100644 --- a/hphp/runtime/base/server/warmup_request_handler.cpp +++ b/hphp/runtime/base/server/warmup_request_handler.cpp @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ - | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/hphp/runtime/base/server/warmup_request_handler.h b/hphp/runtime/base/server/warmup_request_handler.h index 48d0dc07d..3eb650f76 100644 --- a/hphp/runtime/base/server/warmup_request_handler.h +++ b/hphp/runtime/base/server/warmup_request_handler.h @@ -2,7 +2,7 @@ +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ - | Copyright (c) 2010- Facebook, Inc. (http://www.facebook.com) | + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | diff --git a/hphp/runtime/ext/asio/asio_session.cpp b/hphp/runtime/ext/asio/asio_session.cpp index 75efe01bc..a0021c4f3 100644 --- a/hphp/runtime/ext/asio/asio_session.cpp +++ b/hphp/runtime/ext/asio/asio_session.cpp @@ -192,6 +192,17 @@ void AsioSession::onGenArrayCreate(c_GenArrayWaitHandle* wait_handle, CVarRef de } } +void AsioSession::onGenVectorCreate(c_GenVectorWaitHandle* wait_handle, CVarRef dependencies) { + assert(m_onGenVectorCreateCallback.get()); + try { + vm_call_user_func( + m_onGenVectorCreateCallback, + CREATE_VECTOR2(wait_handle, dependencies)); + } catch (const Object& callback_exception) { + raise_warning("[asio] Ignoring exception thrown by GenVectorWaitHandle::onCreate callback"); + } +} + void AsioSession::onSetResultToRefCreate(c_SetResultToRefWaitHandle* wait_handle, CObjRef child) { assert(m_onSetResultToRefCreateCallback.get()); try { diff --git a/hphp/runtime/ext/asio/asio_session.h b/hphp/runtime/ext/asio/asio_session.h index 70520e8a4..7d606bab0 100644 --- a/hphp/runtime/ext/asio/asio_session.h +++ b/hphp/runtime/ext/asio/asio_session.h @@ -30,6 +30,7 @@ namespace HPHP { FORWARD_DECLARE_CLASS_BUILTIN(WaitHandle); FORWARD_DECLARE_CLASS_BUILTIN(GenArrayWaitHandle); +FORWARD_DECLARE_CLASS_BUILTIN(GenVectorWaitHandle); FORWARD_DECLARE_CLASS_BUILTIN(SetResultToRefWaitHandle); FORWARD_DECLARE_CLASS_BUILTIN(ContinuationWaitHandle); FORWARD_DECLARE_CLASS_BUILTIN(ExternalThreadEventWaitHandle); @@ -131,6 +132,14 @@ class AsioSession { bool hasOnGenArrayCreateCallback() { return m_onGenArrayCreateCallback.get(); } void onGenArrayCreate(c_GenArrayWaitHandle* wait_handle, CVarRef dependencies); + // GenVectorWaitHandle callbacks: + void setOnGenVectorCreateCallback(ObjectData* on_create) { + assert(!on_create || on_create->instanceof(c_Closure::s_cls)); + m_onGenVectorCreateCallback = on_create; + } + bool hasOnGenVectorCreateCallback() { return m_onGenVectorCreateCallback.get(); } + void onGenVectorCreate(c_GenVectorWaitHandle* wait_handle, CVarRef dependencies); + // SetResultToRefWaitHandle callbacks: void setOnSetResultToRefCreateCallback(ObjectData* on_create) { assert(!on_create || on_create->instanceof(c_Closure::s_cls)); @@ -158,6 +167,7 @@ class AsioSession { Object m_onContinuationSuccessCallback; Object m_onContinuationFailCallback; Object m_onGenArrayCreateCallback; + Object m_onGenVectorCreateCallback; Object m_onSetResultToRefCreateCallback; Object m_onJoinCallback; diff --git a/hphp/runtime/ext/asio/gen_vector_wait_handle.cpp b/hphp/runtime/ext/asio/gen_vector_wait_handle.cpp new file mode 100644 index 000000000..773e959da --- /dev/null +++ b/hphp/runtime/ext/asio/gen_vector_wait_handle.cpp @@ -0,0 +1,231 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | + | Copyright (c) 1997-2010 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include +#include +#include +#include + +namespace HPHP { +/////////////////////////////////////////////////////////////////////////////// + +namespace { + StaticString s_genVector(""); + + void putException(Object& exception_field, ObjectData* new_exception) { + assert(new_exception); + assert(new_exception->instanceof(SystemLib::s_ExceptionClass)); + + if (exception_field.isNull()) { + exception_field = new_exception; + } + } +} + +c_GenVectorWaitHandle::c_GenVectorWaitHandle(Class* cb) + : c_BlockableWaitHandle(cb), m_exception() { +} + +c_GenVectorWaitHandle::~c_GenVectorWaitHandle() { +} + +void c_GenVectorWaitHandle::t___construct() { + Object e(SystemLib::AllocInvalidOperationExceptionObject( + "Use GenVectorWaitHandle::create() instead of constructor")); + throw e; +} + +void c_GenVectorWaitHandle::ti_setoncreatecallback(CVarRef callback) { + if (!callback.isNull() && !callback.instanceof(c_Closure::s_cls)) { + Object e(SystemLib::AllocInvalidArgumentExceptionObject( + "Unable to set GenVectorWaitHandle::onCreate: on_create_cb not a closure")); + throw e; + } + AsioSession::Get()->setOnGenVectorCreateCallback(callback.getObjectDataOrNull()); +} + +Object c_GenVectorWaitHandle::ti_create(CVarRef dependencies) { + if (UNLIKELY(!dependencies.instanceof(c_Vector::s_cls))) { + Object e(SystemLib::AllocInvalidArgumentExceptionObject( + "Expected dependencies to be an instance of Vector")); + throw e; + } + assert(dynamic_cast(dependencies.getObjectData())); + p_Vector deps = static_cast(dependencies.getObjectData())->clone(); + for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { + TypedValue* current = deps->at(iter_pos); + + if (!c_WaitHandle::fromTypedValue(current)) { + Object e(SystemLib::AllocInvalidArgumentExceptionObject( + "Expected dependencies to be a vector of WaitHandle instances")); + throw e; + } + } + + Object exception; + for (int64_t iter_pos = 0; iter_pos < deps->size(); ++iter_pos) { + + TypedValue* current = deps->at(iter_pos); + assert(current->m_type == KindOfObject); + assert(dynamic_cast(current->m_data.pobj)); + auto child = static_cast(current->m_data.pobj); + + if (child->isSucceeded()) { + tvSetIgnoreRef(child->getResult(), current); + } else if (child->isFailed()) { + putException(exception, child->getException()); + } else { + assert(dynamic_cast(child)); + auto child_wh = static_cast(child); + + p_GenVectorWaitHandle my_wh = NEWOBJ(c_GenVectorWaitHandle)(); + my_wh->initialize(exception, deps.get(), iter_pos, child_wh); + AsioSession* session = AsioSession::Get(); + if (UNLIKELY(session->hasOnGenVectorCreateCallback())) { + session->onGenVectorCreate(my_wh.get(), dependencies); + } + return my_wh; + } + } + + if (exception.isNull()) { + TypedValue tv; + tv.m_type = KindOfObject; + tv.m_data.pobj = deps.get(); + return c_StaticResultWaitHandle::Create(&tv); + } else { + return c_StaticExceptionWaitHandle::Create(exception.get()); + } +} + +void c_GenVectorWaitHandle::initialize(CObjRef exception, c_Vector* deps, int64_t iter_pos, c_WaitableWaitHandle* child) { + m_exception = exception; + m_deps = deps; + m_iterPos = iter_pos; + try { + blockOn(child); + } catch (const Object& cycle_exception) { + putException(m_exception, cycle_exception.get()); + ++m_iterPos; + onUnblocked(); + } +} + +void c_GenVectorWaitHandle::onUnblocked() { + for (; m_iterPos < m_deps->size(); ++m_iterPos) { + + TypedValue* current = m_deps->at(m_iterPos); + assert(current->m_type == KindOfObject); + assert(dynamic_cast(current->m_data.pobj)); + auto child = static_cast(current->m_data.pobj); + + if (child->isSucceeded()) { + tvSetIgnoreRef(child->getResult(), current); + } else if (child->isFailed()) { + putException(m_exception, child->getException()); + } else { + assert(dynamic_cast(child)); + auto child_wh = static_cast(child); + + try { + blockOn(child_wh); + return; + } catch (const Object& cycle_exception) { + putException(m_exception, cycle_exception.get()); + } + } + } + + if (m_exception.isNull()) { + TypedValue result; + result.m_type = KindOfObject; + result.m_data.pobj = m_deps.get(); + setResult(&result); + m_deps = nullptr; + } else { + setException(m_exception.get()); + m_exception = nullptr; + m_deps = nullptr; + } +} + +String c_GenVectorWaitHandle::getName() { + return s_genVector; +} + +c_WaitableWaitHandle* c_GenVectorWaitHandle::getChild() { + assert(getState() == STATE_BLOCKED); + return static_cast(m_deps->at(m_iterPos)->m_data.pobj); +} + +void c_GenVectorWaitHandle::enterContext(context_idx_t ctx_idx) { + assert(AsioSession::Get()->getContext(ctx_idx)); + + // stop before corrupting unioned data + if (isFinished()) { + return; + } + + // already in the more specific context? + if (LIKELY(getContextIdx() >= ctx_idx)) { + return; + } + + assert(getState() == STATE_BLOCKED); + + // recursively import current child + { + assert(m_iterPos < m_deps->size()); + TypedValue* current = m_deps->at(m_iterPos); + + assert(current->m_type == KindOfObject); + assert(dynamic_cast(current->m_data.pobj)); + auto child_wh = static_cast(current->m_data.pobj); + child_wh->enterContext(ctx_idx); + } + + // import ourselves + setContextIdx(ctx_idx); + + // try to import other children + try { + for (int64_t iter_pos = m_iterPos + 1; + iter_pos < m_deps->size(); + ++iter_pos) { + + TypedValue* current = m_deps->at(iter_pos); + assert(current->m_type == KindOfObject); + assert(dynamic_cast(current->m_data.pobj)); + auto child = static_cast(current->m_data.pobj); + + if (child->isFinished()) { + continue; + } + + assert(dynamic_cast(child)); + auto child_wh = static_cast(child); + child_wh->enterContext(ctx_idx); + } + } catch (const Object& cycle_exception) { + // exception will be eventually processed by onUnblocked() + } +} + +/////////////////////////////////////////////////////////////////////////////// +} diff --git a/hphp/runtime/ext/ext_asio.h b/hphp/runtime/ext/ext_asio.h index a3c7a3bee..f2efcb221 100644 --- a/hphp/runtime/ext/ext_asio.h +++ b/hphp/runtime/ext/ext_asio.h @@ -46,6 +46,7 @@ void f_asio_set_on_started_callback(CVarRef on_started_cb); * BlockableWaitHandle - wait handle that can be blocked by other WH * ContinuationWaitHandle - Continuation-powered asynchronous execution * GenArrayWaitHandle - wait handle representing an array of WHs + * GenVectorWaitHandle - wait handle representing an Vector of WHs * SetResultToRefWaitHandle - wait handle that sets result to reference * RescheduleWaitHandle - wait handle that reschedules execution * @@ -343,6 +344,44 @@ class c_GenArrayWaitHandle : public c_BlockableWaitHandle { Array m_deps; ssize_t m_iterPos; }; +// +/////////////////////////////////////////////////////////////////////////////// +// class GenVectorWaitHandle + +/** + * A wait handle that waits for a vector of wait handles. The wait handle + * finishes once all wait handles in the vector are finished. The result value + * preserves order of the original vector. If one of the wait handles failed, + * the exception is propagated by failure. + */ +FORWARD_DECLARE_CLASS_BUILTIN(GenVectorWaitHandle); +FORWARD_DECLARE_CLASS_BUILTIN(Vector); +class c_GenVectorWaitHandle : public c_BlockableWaitHandle { + public: + DECLARE_CLASS(GenVectorWaitHandle, GenVectorWaitHandle, BlockableWaitHandle) + + // need to implement + public: c_GenVectorWaitHandle(Class* cls = c_GenVectorWaitHandle::s_cls); + public: ~c_GenVectorWaitHandle(); + public: void t___construct(); + public: static void ti_setoncreatecallback(CVarRef callback); + public: static Object ti_create(CVarRef dependencies); + + public: + String getName(); + void enterContext(context_idx_t ctx_idx); + + protected: + void onUnblocked(); + c_WaitableWaitHandle* getChild(); + + private: + void initialize(CObjRef exception, c_Vector* deps, int64_t iter_pos, c_WaitableWaitHandle* child); + + Object m_exception; + p_Vector m_deps; + int64_t m_iterPos; +}; /////////////////////////////////////////////////////////////////////////////// // class SetResultToRefWaitHandle diff --git a/hphp/system/class_map.cpp b/hphp/system/class_map.cpp index 1f483e57e..5e175495c 100644 --- a/hphp/system/class_map.cpp +++ b/hphp/system/class_map.cpp @@ -24415,6 +24415,30 @@ const char *g_class_map[] = { NULL, NULL, NULL, + (const char *)0x10006000, "GenVectorWaitHandle", "blockablewaithandle", "", (const char *)0, (const char *)0, + "/**\n * ( excerpt from http://php.net/manual/en/class.genvectorwaithandle.php )\n *\n * A wait handle representing a vector of asynchronous operations\n *\n */", + NULL, + (const char *)0x10006100, "__construct", "", (const char*)0, (const char*)0, + " /**\n * ( excerpt from\n * http://php.net/manual/en/genvectorwaithandle.construct.php )\n *\n *\n */", + (const char *)0x8 /* KindOfNull */, NULL, + NULL, + NULL, + (const char *)0x10006240, "create", "", (const char*)0, (const char*)0, + " /**\n * ( excerpt from http://php.net/manual/en/genvectorwaithandle.create.php )\n *\n * Create a wait handle that waits for a given vector of dependencies\n *\n * @dependencies\n * mixed A Vector of dependencies to wait for\n *\n * @return object A WaitHandle that will wait for a given vector of\n * dependencies and return their results\n */", + (const char *)0x40 /* KindOfObject */, (const char *)0x2000, "dependencies", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "", (const char *)0, "", (const char *)0, NULL, + NULL, + NULL, + NULL, + (const char *)0x10016240, "setOnCreateCallback", "", (const char*)0, (const char*)0, + " /**\n * ( HipHop specific )\n *\n * Set callback for when a GenVectorWaitHandle is created\n *\n * @on_create_cb\n * mixed A Closure to be called on creation\n */", + (const char *)0x8 /* KindOfNull */, (const char *)0x2000, "on_create_cb", "", (const char *)0xffffffff /* KindOfUnknown: $t: Variant */, "", (const char *)0, "", (const char *)0, NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, (const char *)0x10006000, "SetResultToRefWaitHandle", "blockablewaithandle", "", (const char *)0, (const char *)0, "/**\n * ( excerpt from\n * http://php.net/manual/en/class.setresulttorefwaithandle.php )\n *\n * A wait handle proxy that sets result to a given reference\n *\n */", NULL, diff --git a/hphp/test/test_ext_asio.cpp b/hphp/test/test_ext_asio.cpp index a4fb8245d..1f20c1201 100644 --- a/hphp/test/test_ext_asio.cpp +++ b/hphp/test/test_ext_asio.cpp @@ -32,6 +32,7 @@ bool TestExtAsio::RunTests(const std::string &which) { RUN_TEST(test_BlockableWaitHandle); RUN_TEST(test_ContinuationWaitHandle); RUN_TEST(test_GenArrayWaitHandle); + RUN_TEST(test_GenVectorWaitHandle); RUN_TEST(test_SetResultToRefWaitHandle); return ret; @@ -75,6 +76,10 @@ bool TestExtAsio::test_GenArrayWaitHandle() { return Count(true); } +bool TestExtAsio::test_GenVectorWaitHandle() { + return Count(true); +} + bool TestExtAsio::test_SetResultToRefWaitHandle() { return Count(true); } diff --git a/hphp/test/test_ext_asio.h b/hphp/test/test_ext_asio.h index 1a182e375..318c62fb2 100644 --- a/hphp/test/test_ext_asio.h +++ b/hphp/test/test_ext_asio.h @@ -39,6 +39,7 @@ class TestExtAsio : public TestCppExt { bool test_BlockableWaitHandle(); bool test_ContinuationWaitHandle(); bool test_GenArrayWaitHandle(); + bool test_GenVectorWaitHandle(); bool test_SetResultToRefWaitHandle(); };