GenVectorWaitHandle
add GenVectorWaitHandle, a WaitHandle suitable for use with the Vector collection.
Esse commit está contido em:
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 <runtime/ext/ext_asio.h>
|
||||
#include <runtime/ext/ext_collections.h>
|
||||
#include <runtime/ext/ext_closure.h>
|
||||
#include <runtime/ext/asio/asio_context.h>
|
||||
#include <runtime/ext/asio/asio_session.h>
|
||||
#include <system/lib/systemlib.h>
|
||||
|
||||
namespace HPHP {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
StaticString s_genVector("<gen-vector>");
|
||||
|
||||
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<c_Vector*>(dependencies.getObjectData()));
|
||||
p_Vector deps = static_cast<c_Vector*>(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<c_WaitHandle*>(current->m_data.pobj));
|
||||
auto child = static_cast<c_WaitHandle*>(current->m_data.pobj);
|
||||
|
||||
if (child->isSucceeded()) {
|
||||
tvSetIgnoreRef(child->getResult(), current);
|
||||
} else if (child->isFailed()) {
|
||||
putException(exception, child->getException());
|
||||
} else {
|
||||
assert(dynamic_cast<c_WaitableWaitHandle*>(child));
|
||||
auto child_wh = static_cast<c_WaitableWaitHandle*>(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<c_WaitHandle*>(current->m_data.pobj));
|
||||
auto child = static_cast<c_WaitHandle*>(current->m_data.pobj);
|
||||
|
||||
if (child->isSucceeded()) {
|
||||
tvSetIgnoreRef(child->getResult(), current);
|
||||
} else if (child->isFailed()) {
|
||||
putException(m_exception, child->getException());
|
||||
} else {
|
||||
assert(dynamic_cast<c_WaitableWaitHandle*>(child));
|
||||
auto child_wh = static_cast<c_WaitableWaitHandle*>(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<c_WaitableWaitHandle*>(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<c_WaitableWaitHandle*>(current->m_data.pobj));
|
||||
auto child_wh = static_cast<c_WaitableWaitHandle*>(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<c_WaitHandle*>(current->m_data.pobj));
|
||||
auto child = static_cast<c_WaitHandle*>(current->m_data.pobj);
|
||||
|
||||
if (child->isFinished()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(dynamic_cast<c_WaitableWaitHandle*>(child));
|
||||
auto child_wh = static_cast<c_WaitableWaitHandle*>(child);
|
||||
child_wh->enterContext(ctx_idx);
|
||||
}
|
||||
} catch (const Object& cycle_exception) {
|
||||
// exception will be eventually processed by onUnblocked()
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class TestExtAsio : public TestCppExt {
|
||||
bool test_BlockableWaitHandle();
|
||||
bool test_ContinuationWaitHandle();
|
||||
bool test_GenArrayWaitHandle();
|
||||
bool test_GenVectorWaitHandle();
|
||||
bool test_SetResultToRefWaitHandle();
|
||||
};
|
||||
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário