diff --git a/doc/options.compiled b/doc/options.compiled index d97bc05693bd2..bab9e8591ac1b 100644 --- a/doc/options.compiled +++ b/doc/options.compiled @@ -457,6 +457,16 @@ A pagelet server is essentially the same as local CURL, except it's more efficient. This allows parallel execution of a web page, preparing two panels or iframes at the same time. + Fiber { + ThreadCount = 0 + } + +- Fiber Asynchronous Functions + +A "fiber" is a sub-thread of a request thread, mainly for implementing +call_user_func_async(). This thread count specifies totally number of physical +threads allocated for executing fiber asynchronous function calls. + = Proxy Server Proxy { diff --git a/src/idl/function.idl.php b/src/idl/function.idl.php index 44b8895fb3e3e..5cdc0ab927458 100644 --- a/src/idl/function.idl.php +++ b/src/idl/function.idl.php @@ -22,19 +22,18 @@ array('function' => Variant), VariableArguments); -f('call_user_func_array_async', Variant, +f('call_user_func_array_async', Object, array('function' => Variant, 'params' => VariantVec)); -f('call_user_func_async', Variant, +f('call_user_func_async', Object, array('function' => Variant), VariableArguments); f('end_user_func_async', Variant, - array('handle' => Variant, + array('handle' => Object, 'strategy' => array(Int32, 'k_GLOBAL_STATE_OVERWRITE'), - 'resolver' => array(Variant, 'null')), - VariableArguments); + 'resolver' => array(Variant, 'null'))); f('forward_static_call_array', Variant, array('function' => Variant, diff --git a/src/runtime/base/fiber_async_func.cpp b/src/runtime/base/fiber_async_func.cpp new file mode 100644 index 0000000000000..17d438a630472 --- /dev/null +++ b/src/runtime/base/fiber_async_func.cpp @@ -0,0 +1,245 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010 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. | + +----------------------------------------------------------------------+ +*/ + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace HPHP { +/////////////////////////////////////////////////////////////////////////////// + +class FiberJob : public Synchronizable { +public: + FiberJob(CVarRef function, CArrRef params) + : m_function(function), m_params(params), m_refCount(0), + m_async(true), m_ready(false), m_done(false), m_exit(false) { + } + + void waitForReady() { + Lock lock(this); + while (!m_ready) wait(); + } + + bool isDone() { + return m_done; + } + + void run(bool async) { + m_async = async; + + // make local copy of m_function and m_params + if (async) { + m_function = m_function.fiberCopy(); + m_params = m_params.fiberCopy(); + + Lock lock(this); + m_ready = true; + notify(); + } + + try { + m_return = f_call_user_func_array(m_function, m_params); + } catch (const ExitException &e) { + m_exit = true; + } catch (const Exception &e) { + m_fatal = String(e.getMessage()); + } catch (Object e) { + m_exception = e; + } catch (...) { + m_fatal = String("unknown exception was thrown"); + } + + Lock lock(this); + m_done = true; + notify(); + } + + Variant getResults(FiberAsyncFunc::Strategy strategy, CVarRef resolver) { + { + Lock lock(this); + while (!m_done) wait(); + } + + if (m_exit) { + throw ExitException(0); + } + if (!m_fatal.isNull()) { + throw FatalErrorException("%s", m_fatal.data()); + } + if (!m_exception.isNull()) { + if (m_async) { + throw m_exception.fiberCopy(); + } else { + throw m_exception; + } + } + if (m_async) { + return m_return.fiberCopy(); + } + return m_return; + } + + // ref counting + void incRefCount() { + Lock lock(m_mutex); + ++m_refCount; + } + void decRefCount() { + { + Lock lock(m_mutex); + --m_refCount; + } + if (m_refCount == 0) { + delete this; + } + } + +private: + Variant m_function; + Array m_params; + + Mutex m_mutex; + int m_refCount; + + bool m_async; + bool m_ready; + bool m_done; + + bool m_exit; + String m_fatal; + Object m_exception; + Variant m_return; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class FiberWorker : public JobQueueWorker { +public: + virtual void doJob(FiberJob *job) { + job->run(true); + job->decRefCount(); + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +class FiberAsyncFuncHandle : public ResourceData { +public: + DECLARE_OBJECT_ALLOCATION(FiberAsyncFuncHandle); + + FiberAsyncFuncHandle(CVarRef function, CArrRef params) { + m_job = new FiberJob(function, params); + m_job->incRefCount(); + } + + ~FiberAsyncFuncHandle() { + m_job->decRefCount(); + } + + FiberJob *getJob() { return m_job;} + + // overriding ResourceData + virtual const char *o_getClassName() const { return "FiberAsyncFuncHandle";} + +private: + FiberJob *m_job; +}; + +IMPLEMENT_OBJECT_ALLOCATION(FiberAsyncFuncHandle); + +/////////////////////////////////////////////////////////////////////////////// +// implementing PageletServer + +static JobQueueDispatcher *s_dispatcher; + +void FiberAsyncFunc::Restart() { + if (s_dispatcher) { + s_dispatcher->stop(); + delete s_dispatcher; + s_dispatcher = NULL; + } + if (RuntimeOption::FiberCount > 0) { + s_dispatcher = new JobQueueDispatcher + (RuntimeOption::FiberCount, NULL); + Logger::Info("fiber job dispatcher started"); + s_dispatcher->start(); + } +} + +Object FiberAsyncFunc::Start(CVarRef function, CArrRef params) { + FiberAsyncFuncHandle *handle = NEW(FiberAsyncFuncHandle)(function, params); + Object ret(handle); + + FiberJob *job = handle->getJob(); + if (s_dispatcher) { + job->incRefCount(); // paired with worker's decRefCount() + s_dispatcher->enqueue(job); + job->waitForReady(); // until job data are copied into fiber + } else { + job->run(false); // immediately executing the job + } + + return ret; +} + +bool FiberAsyncFunc::Status(CObjRef func) { + FiberAsyncFuncHandle *handle = func.getTyped(); + return handle->getJob()->isDone(); +} + +Variant FiberAsyncFunc::Result(CObjRef func, Strategy strategy, + CVarRef resolver) { + FiberAsyncFuncHandle *handle = func.getTyped(); + return handle->getJob()->getResults(strategy, resolver); +} + +/////////////////////////////////////////////////////////////////////////////// + +static IMPLEMENT_THREAD_LOCAL(PointerMap, s_forward_references); +static IMPLEMENT_THREAD_LOCAL(PointerMap, s_reverse_references); + +void *FiberReferenceMap::Lookup(void *src) { + PointerMap::iterator iter = s_forward_references->find(src); + if (iter != s_forward_references->end()) { + return iter->second; + } + return NULL; +} + +void *FiberReferenceMap::ReverseLookup(void *copy) { + PointerMap::iterator iter = s_reverse_references->find(copy); + if (iter != s_reverse_references->end()) { + return iter->second; + } + return NULL; +} + +void FiberReferenceMap::Insert(void *src, void *copy) { + ASSERT(Lookup(src) == NULL); + ASSERT(copy == NULL || ReverseLookup(copy) == NULL); + (*s_forward_references)[src] = copy; + if (copy) { + (*s_reverse_references)[copy] = src; + } +} + +/////////////////////////////////////////////////////////////////////////////// +} diff --git a/src/runtime/base/fiber_async_func.h b/src/runtime/base/fiber_async_func.h new file mode 100644 index 0000000000000..3d9aba61f2234 --- /dev/null +++ b/src/runtime/base/fiber_async_func.h @@ -0,0 +1,72 @@ +/* + +----------------------------------------------------------------------+ + | HipHop for PHP | + +----------------------------------------------------------------------+ + | Copyright (c) 2010 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. | + +----------------------------------------------------------------------+ +*/ + +#ifndef __HPHP_FIBER_H__ +#define __HPHP_FIBER_H__ + +#include + +namespace HPHP { +/////////////////////////////////////////////////////////////////////////////// + +class FiberAsyncFunc { +public: + // These values have to be consistent with what's in system/globals/ + // constants.php, which was generated by bin/gen_constants.php + enum Strategy { + GlobalStateIgnore = 0, + GlobalStateOverwrite = 1, + GlobalStateSkip = 2, + GlobalStateResolveConflict = 3, + }; + +public: + static void Restart(); + + /** + * Create an asynchronous function call. This always returns a handle. + */ + static Object Start(CVarRef function, CArrRef params); + + /** + * Query if an async call is finished. This is non-blocking and can be + * called as many times as desired. + */ + static bool Status(CObjRef func); + + /** + * Get results of an async call. This is blocking until task is finished. + */ + static Variant Result(CObjRef func, Strategy strategy, CVarRef resolver); +}; + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Referenced pointer (strongly bound variants and objects) mapping between + * mother thread and fiber. + */ +class FiberReferenceMap { +public: + static void *Lookup(void *src); + static void *ReverseLookup(void *copy); + static void Insert(void *src, void *copy); +}; + +/////////////////////////////////////////////////////////////////////////////// +} + +#endif // __HPHP_FIBER_H__ diff --git a/src/runtime/base/resource_data.cpp b/src/runtime/base/resource_data.cpp index e820908d9d714..1b55355d27d1b 100644 --- a/src/runtime/base/resource_data.cpp +++ b/src/runtime/base/resource_data.cpp @@ -26,7 +26,6 @@ String ResourceData::t___tostring() { } ObjectData* ResourceData::cloneImpl() { - ASSERT(false); return NULL; } diff --git a/src/runtime/base/runtime_option.cpp b/src/runtime/base/runtime_option.cpp index 9de4fdfb49398..4c3e063ee7ae8 100644 --- a/src/runtime/base/runtime_option.cpp +++ b/src/runtime/base/runtime_option.cpp @@ -76,6 +76,7 @@ std::string RuntimeOption::ServerPrimaryIP; int RuntimeOption::ServerPort; int RuntimeOption::ServerThreadCount = 50; int RuntimeOption::PageletServerThreadCount = 0; +int RuntimeOption::FiberCount = 0; int RuntimeOption::RequestTimeoutSeconds = 0; int RuntimeOption::RequestMemoryMaxBytes = 0; int RuntimeOption::ImageMemoryMaxBytes = 0; @@ -629,6 +630,7 @@ void RuntimeOption::Load(Hdf &config) { } { PageletServerThreadCount = config["PageletServer.ThreadCount"].getInt32(0); + FiberCount = config["Fiber.ThreadCount"].getInt32(0); } { Hdf content = config["StaticFile"]; diff --git a/src/runtime/base/runtime_option.h b/src/runtime/base/runtime_option.h index 3a3a6ffe24f6b..b319864552077 100644 --- a/src/runtime/base/runtime_option.h +++ b/src/runtime/base/runtime_option.h @@ -74,6 +74,7 @@ class RuntimeOption { static int ServerPort; static int ServerThreadCount; static int PageletServerThreadCount; + static int FiberCount; static int RequestTimeoutSeconds; static int RequestMemoryMaxBytes; static int ImageMemoryMaxBytes; diff --git a/src/runtime/base/type_array.cpp b/src/runtime/base/type_array.cpp index d7dc2085b474c..55fff49553e55 100644 --- a/src/runtime/base/type_array.cpp +++ b/src/runtime/base/type_array.cpp @@ -754,6 +754,13 @@ void Array::unserialize(VariableUnserializer *unserializer) { } } +Array Array::fiberCopy() { + if (m_px) { + return m_px->copy(); + } + return Array(); +} + void Array::dump() { if (m_px) { m_px->dump(); diff --git a/src/runtime/base/type_array.h b/src/runtime/base/type_array.h index b7418d08c49e1..c1e2cde7c8525 100644 --- a/src/runtime/base/type_array.h +++ b/src/runtime/base/type_array.h @@ -421,6 +421,11 @@ class Array : public SmartPtr { void serialize(VariableSerializer *serializer) const; void unserialize(VariableUnserializer *in); + /** + * Used by FiberAsyncFunc to copy in the new fiber. + */ + Array fiberCopy(); + /** * Memory allocator methods. */ diff --git a/src/runtime/base/type_object.cpp b/src/runtime/base/type_object.cpp index 9e2bb684ad721..d0fdb14264b43 100644 --- a/src/runtime/base/type_object.cpp +++ b/src/runtime/base/type_object.cpp @@ -19,6 +19,7 @@ #include #include #include +#include namespace HPHP { @@ -84,5 +85,20 @@ bool Object::unserialize(std::istream &in) { throw NotImplementedException(__func__); } +Object Object::fiberCopy() { + if (m_px) { + if (m_px->getCount() > 1) { + ObjectData *px = (ObjectData*)FiberReferenceMap::Lookup(m_px); + if (px == NULL) { + px = m_px->clone(); + FiberReferenceMap::Insert(m_px, px); + } + return px; + } + return m_px->clone(); + } + return Object(); +} + /////////////////////////////////////////////////////////////////////////////// } diff --git a/src/runtime/base/type_object.h b/src/runtime/base/type_object.h index 7b1b2be182d76..2632301a59729 100644 --- a/src/runtime/base/type_object.h +++ b/src/runtime/base/type_object.h @@ -136,6 +136,11 @@ class Object : public SmartPtr { void serialize(VariableSerializer *serializer) const; bool unserialize(std::istream &in); + /** + * Used by FiberAsyncFunc to copy in the new fiber. + */ + Object fiberCopy(); + private: static void compileTimeAssertions() { CT_ASSERT(offsetof(Object, m_px) == offsetof(Value, m_data)); diff --git a/src/runtime/base/type_string.cpp b/src/runtime/base/type_string.cpp index b51e7b522db3c..e9352630d8539 100644 --- a/src/runtime/base/type_string.cpp +++ b/src/runtime/base/type_string.cpp @@ -621,6 +621,13 @@ void String::unserialize(std::istream &in, } } +String String::fiberCopy() { + if (m_px) { + return m_px->copy(); + } + return String(); +} + /////////////////////////////////////////////////////////////////////////////// // debugging diff --git a/src/runtime/base/type_string.h b/src/runtime/base/type_string.h index f49c1e96e2130..6a84e40a133cf 100644 --- a/src/runtime/base/type_string.h +++ b/src/runtime/base/type_string.h @@ -264,6 +264,11 @@ class String : public SmartPtr { void unserialize(std::istream &in, char delimiter0 = '"', char delimiter1 = '"'); + /** + * Used by FiberAsyncFunc to copy in the new fiber. + */ + String fiberCopy(); + /** * Debugging */ diff --git a/src/runtime/base/type_variant.cpp b/src/runtime/base/type_variant.cpp index 9f0c5ab2c2d5a..e8e0e8a51f1e6 100644 --- a/src/runtime/base/type_variant.cpp +++ b/src/runtime/base/type_variant.cpp @@ -23,6 +23,7 @@ #include #include #include +#include using namespace std; @@ -2890,6 +2891,45 @@ Variant Variant::share(bool save) const { return false; // same as non-existent } +Variant Variant::fiberCopy() { + if (m_type == KindOfVariant) { + Variant *mpvar = m_data.pvar; + if (mpvar->getCount() > 1) { + Variant *pvar = (Variant*)FiberReferenceMap::Lookup(mpvar); + if (pvar == NULL) { + pvar = NEW(Variant)(); + *pvar = mpvar->fiberCopy(); + FiberReferenceMap::Insert(mpvar, pvar); + } + return pvar; + } + return mpvar->fiberCopy(); + } + + switch (m_type) { + case KindOfNull: return Variant(); + case KindOfBoolean: return (m_data.num != 0); + case KindOfByte: + case KindOfInt16: + case KindOfInt32: + case KindOfInt64: return m_data.num; + case KindOfDouble: return m_data.dbl; + case LiteralString: return m_data.str; + case KindOfStaticString: + case KindOfString: + return String(m_data.pstr->data(), m_data.pstr->size(), CopyString); + case KindOfArray: + return Array(m_data.parr->copy()); + case KindOfObject: + return Object(m_data.pobj).fiberCopy(); + default: + ASSERT(false); + break; + } + + return Variant(); +} + const char *Variant::getTypeString(DataType type) { switch (type) { case KindOfNull: return "KindOfNull"; diff --git a/src/runtime/base/type_variant.h b/src/runtime/base/type_variant.h index 4b0bfd2eaee24..076623b9e8477 100644 --- a/src/runtime/base/type_variant.h +++ b/src/runtime/base/type_variant.h @@ -481,6 +481,11 @@ class Variant { */ Variant share(bool save) const; + /** + * Used by FiberAsyncFunc to copy in the new fiber. + */ + Variant fiberCopy(); + /** * Debugging functions. */ diff --git a/src/runtime/ext/ext_function.cpp b/src/runtime/ext/ext_function.cpp index a73a34c3f906d..5d6af5401f366 100644 --- a/src/runtime/ext/ext_function.cpp +++ b/src/runtime/ext/ext_function.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -101,16 +102,20 @@ Variant f_call_user_func(int _argc, CVarRef function, CArrRef _argv /* = null_ar return f_call_user_func_array(function, _argv); } -Variant f_call_user_func_array_async(CVarRef function, CArrRef params) { - throw NotImplementedException(__func__); +Object f_call_user_func_array_async(CVarRef function, CArrRef params) { + return f_call_user_func_async(0, function, params); } -Variant f_call_user_func_async(int _argc, CVarRef function, CArrRef _argv /* = null_array */) { - throw NotImplementedException(__func__); +Object f_call_user_func_async(int _argc, CVarRef function, + CArrRef _argv /* = null_array */) { + return FiberAsyncFunc::Start(function, _argv); } -Variant f_end_user_func_async(int _argc, CVarRef handle, int strategy /* = k_GLOBAL_STATE_OVERWRITE */, CVarRef resolver /* = null */, CArrRef _argv /* = null_array */) { - throw NotImplementedException(__func__); +Variant f_end_user_func_async(CObjRef handle, + int strategy /* = k_GLOBAL_STATE_OVERWRITE */, + CVarRef resolver /* = null */) { + return FiberAsyncFunc::Result(handle, (FiberAsyncFunc::Strategy)strategy, + resolver); } Variant f_forward_static_call_array(CVarRef function, CArrRef params) { diff --git a/src/runtime/ext/ext_function.h b/src/runtime/ext/ext_function.h index b97d2dd0a6557..822e577419c2e 100644 --- a/src/runtime/ext/ext_function.h +++ b/src/runtime/ext/ext_function.h @@ -28,11 +28,11 @@ bool f_function_exists(CStrRef function_name); bool f_is_callable(CVarRef v, bool syntax = false, Variant name = null); Variant f_call_user_func(int _argc, CVarRef function, CArrRef _argv = null_array); -Variant f_call_user_func_array_async(CVarRef function, CArrRef params); -Variant f_call_user_func_async(int _argc, CVarRef function, CArrRef _argv = null_array); +Object f_call_user_func_array_async(CVarRef function, CArrRef params); +Object f_call_user_func_async(int _argc, CVarRef function, CArrRef _argv = null_array); Variant f_forward_static_call_array(CVarRef function, CArrRef params); Variant f_forward_static_call(int _argc, CVarRef function, CArrRef _argv = null_array); -Variant f_end_user_func_async(int _argc, CVarRef handle, int strategy = k_GLOBAL_STATE_OVERWRITE, CVarRef resolver = null, CArrRef _argv = null_array); +Variant f_end_user_func_async(CObjRef handle, int strategy = k_GLOBAL_STATE_OVERWRITE, CVarRef resolver = null); String f_create_function(CStrRef args, CStrRef code); /////////////////////////////////////////////////////////////////////////////// diff --git a/src/runtime/ext/profile/extprofile_function.h b/src/runtime/ext/profile/extprofile_function.h index 3797cfc25f82e..06c48e32cb838 100644 --- a/src/runtime/ext/profile/extprofile_function.h +++ b/src/runtime/ext/profile/extprofile_function.h @@ -50,19 +50,19 @@ inline Variant x_call_user_func(int _argc, CVarRef function, CArrRef _argv = nul return f_call_user_func(_argc, function, _argv); } -inline Variant x_call_user_func_array_async(CVarRef function, CArrRef params) { +inline Object x_call_user_func_array_async(CVarRef function, CArrRef params) { FUNCTION_INJECTION_BUILTIN(call_user_func_array_async); return f_call_user_func_array_async(function, params); } -inline Variant x_call_user_func_async(int _argc, CVarRef function, CArrRef _argv = null_array) { +inline Object x_call_user_func_async(int _argc, CVarRef function, CArrRef _argv = null_array) { FUNCTION_INJECTION_BUILTIN(call_user_func_async); return f_call_user_func_async(_argc, function, _argv); } -inline Variant x_end_user_func_async(int _argc, CVarRef handle, int strategy = k_GLOBAL_STATE_OVERWRITE, CVarRef resolver = null, CArrRef _argv = null_array) { +inline Variant x_end_user_func_async(CObjRef handle, int strategy = k_GLOBAL_STATE_OVERWRITE, CVarRef resolver = null) { FUNCTION_INJECTION_BUILTIN(end_user_func_async); - return f_end_user_func_async(_argc, handle, strategy, resolver, _argv); + return f_end_user_func_async(handle, strategy, resolver); } inline Variant x_forward_static_call_array(CVarRef function, CArrRef params) { diff --git a/src/system/function.inc b/src/system/function.inc index b372784f2ff57..c432dea0b5808 100644 --- a/src/system/function.inc +++ b/src/system/function.inc @@ -4,9 +4,9 @@ "is_callable", T(Boolean), S(0), "v", T(Some), NULL, S(0), "syntax", T(Boolean), "false", S(0), "name", T(Variant), "null", S(1), NULL, S(0), "call_user_func_array", T(Variant), S(0), "function", T(Variant), NULL, S(0), "params", T(Array), NULL, S(0), NULL, S(0), "call_user_func", T(Variant), S(0), "function", T(Variant), NULL, S(0), NULL, S(1), -"call_user_func_array_async", T(Variant), S(0), "function", T(Variant), NULL, S(0), "params", T(Array), NULL, S(0), NULL, S(0), -"call_user_func_async", T(Variant), S(0), "function", T(Variant), NULL, S(0), NULL, S(1), -"end_user_func_async", T(Variant), S(0), "handle", T(Variant), NULL, S(0), "strategy", T(Int32), "k_GLOBAL_STATE_OVERWRITE", S(0), "resolver", T(Variant), "null", S(0), NULL, S(1), +"call_user_func_array_async", T(Object), S(0), "function", T(Variant), NULL, S(0), "params", T(Array), NULL, S(0), NULL, S(0), +"call_user_func_async", T(Object), S(0), "function", T(Variant), NULL, S(0), NULL, S(1), +"end_user_func_async", T(Variant), S(0), "handle", T(Object), NULL, S(0), "strategy", T(Int32), "k_GLOBAL_STATE_OVERWRITE", S(0), "resolver", T(Variant), "null", S(0), NULL, S(0), "forward_static_call_array", T(Variant), S(0), "function", T(Variant), NULL, S(0), "params", T(Array), NULL, S(0), NULL, S(0), "forward_static_call", T(Variant), S(0), "function", T(Variant), NULL, S(0), NULL, S(1), "create_function", T(String), S(0), "args", T(String), NULL, S(0), "code", T(String), NULL, S(0), NULL, S(0), diff --git a/src/system/gen/sys/dynamic_table_func.no.cpp b/src/system/gen/sys/dynamic_table_func.no.cpp index 7dccdab58123a..99808c227a9cd 100644 --- a/src/system/gen/sys/dynamic_table_func.no.cpp +++ b/src/system/gen/sys/dynamic_table_func.no.cpp @@ -2713,11 +2713,10 @@ Variant i_libxml_use_internal_errors(CArrRef params) { Variant i_end_user_func_async(CArrRef params) { FUNCTION_INJECTION(end_user_func_async); int count __attribute__((__unused__)) = params.size(); - if (count < 1) return throw_missing_arguments("end_user_func_async", count+1, 1); - if (count <= 1) return (f_end_user_func_async(count, params[0])); - if (count == 2) return (f_end_user_func_async(count, params[0], params[1])); - if (count == 3) return (f_end_user_func_async(count, params[0], params[1], params[2])); - return (f_end_user_func_async(count,params[0], params[1], params[2], params.slice(3, count - 3, false))); + if (count < 1 || count > 3) return throw_wrong_arguments("end_user_func_async", count, 1, 3, 1); + if (count <= 1) return (f_end_user_func_async(params[0])); + if (count == 2) return (f_end_user_func_async(params[0], params[1])); + return (f_end_user_func_async(params[0], params[1], params[2])); } Variant i_openssl_get_publickey(CArrRef params) { FUNCTION_INJECTION(openssl_get_publickey); @@ -26076,7 +26075,7 @@ Variant ei_end_user_func_async(Eval::VariableEnvironment &env, const Eval::Funct Variant a2; const std::vector ¶ms = caller->params(); int count __attribute__((__unused__)) = params.size(); - if (count < 1) return throw_missing_arguments("end_user_func_async", count+1, 1); + if (count < 1 || count > 3) return throw_wrong_arguments("end_user_func_async", count, 1, 3, 1); std::vector::const_iterator it = params.begin(); do { if (it == params.end()) break; @@ -26089,14 +26088,12 @@ Variant ei_end_user_func_async(Eval::VariableEnvironment &env, const Eval::Funct a2 = (*it)->eval(env); it++; } while(false); - Array vargs; for (; it != params.end(); ++it) { - vargs.append((*it)->eval(env)); + (*it)->eval(env); } - if (count <= 1) return (x_end_user_func_async(count, a0)); - else if (count == 2) return (x_end_user_func_async(count, a0, a1)); - else if (count == 3) return (x_end_user_func_async(count, a0, a1, a2)); - return (x_end_user_func_async(count, a0,a1, a2, vargs)); + if (count <= 1) return (x_end_user_func_async(a0)); + else if (count == 2) return (x_end_user_func_async(a0, a1)); + else return (x_end_user_func_async(a0, a1, a2)); } Variant ei_openssl_get_publickey(Eval::VariableEnvironment &env, const Eval::FunctionCallExpression *caller) { Variant a0; diff --git a/src/test/test_ext_function.cpp b/src/test/test_ext_function.cpp index 9aa2a8b2b155a..4a6b1e9d0eafb 100644 --- a/src/test/test_ext_function.cpp +++ b/src/test/test_ext_function.cpp @@ -27,6 +27,11 @@ bool TestExtFunction::RunTests(const std::string &which) { RUN_TEST(test_is_callable); RUN_TEST(test_call_user_func_array); RUN_TEST(test_call_user_func); + RUN_TEST(test_call_user_func_array_async); + RUN_TEST(test_call_user_func_async); + RUN_TEST(test_end_user_func_async); + RUN_TEST(test_forward_static_call_array); + RUN_TEST(test_forward_static_call); RUN_TEST(test_create_function); RUN_TEST(test_func_get_arg); RUN_TEST(test_func_get_args); @@ -73,6 +78,43 @@ bool TestExtFunction::test_call_user_func() { return Count(true); } +bool TestExtFunction::test_call_user_func_array_async() { + Array params = CREATE_VECTOR1("param"); + { + RuntimeOption::FiberCount = 0; + Object handle = f_call_user_func_array_async("Test", params); + Variant ret = f_end_user_func_async(handle); + VS(ret, "param"); + } + { + RuntimeOption::FiberCount = 1; + Object handle = f_call_user_func_array_async("Test", params); + Variant ret = f_end_user_func_async(handle); + VS(ret, "param"); + } + return Count(true); +} + +bool TestExtFunction::test_call_user_func_async() { + // tested in test_call_user_func_array_async + return true; +} + +bool TestExtFunction::test_end_user_func_async() { + // tested in test_call_user_func_array_async + return true; +} + +bool TestExtFunction::test_forward_static_call_array() { + // tested in TestCodeRun::TestLateStaticBinding + return true; +} + +bool TestExtFunction::test_forward_static_call() { + // tested in TestCodeRun::TestLateStaticBinding + return true; +} + bool TestExtFunction::test_create_function() { try { f_create_function("test", ""); diff --git a/src/test/test_ext_function.h b/src/test/test_ext_function.h index 1848c9751c311..88f0f6461fb63 100644 --- a/src/test/test_ext_function.h +++ b/src/test/test_ext_function.h @@ -32,6 +32,11 @@ class TestExtFunction : public TestCppExt { bool test_is_callable(); bool test_call_user_func_array(); bool test_call_user_func(); + bool test_call_user_func_array_async(); + bool test_call_user_func_async(); + bool test_end_user_func_async(); + bool test_forward_static_call_array(); + bool test_forward_static_call(); bool test_create_function(); bool test_func_get_arg(); bool test_func_get_args(); diff --git a/src/util/base.h b/src/util/base.h index 76e06d147b3af..f4fe6a9b3c88f 100644 --- a/src/util/base.h +++ b/src/util/base.h @@ -135,6 +135,7 @@ class hphp_string_map : typedef hphp_hash_set, eqstr> hphp_const_char_set; +typedef hphp_hash_map > PointerMap; typedef hphp_hash_map > PointerCounterMap; typedef hphp_hash_set > PointerSet;