AsioExternalThreadEvent::unserialize() may legally throw PHP exceptions.
When that happens, construction of PHP exceptions reenters VM, surprised
flag is checked and pending C++ exceptions may be thrown.
Let's make sure the queue of received events in runUntil() is not lost.
Move this responsibility to the AsioExternalThreadEventQueue.
External thread event queueing logic is big enough that it deserves its
own class. Move the code and rename members to better reflect the
underlying operations. Implementation is otherwise unchanged.
I noticed that directorty structure of hphp/system was a bit scattered, so
I consolidated things to reduce the total number of folders and to put
related things together with each other.
This diff moves the contents of "hphp/system/classes_hhvm" into
"hphp/system", it moves the contents of "hphp/system/lib" into
"hphp/system", moves "hphp/idl" to "hphp/system/idl", and moves the
contents of "hphp/system/globals" into "hphp/system/idl".
Recover from C++ exceptions so that PSP does not think ASIO is running. Fixes a segfault when ASIO fails on internal invariant violation in runUntil() with m_current == nullptr.
- smart::make_unique, returns a unique pointer to smart allocated
data.
- Support forwarding in our allocator's construct, so
smart::vector<foo::unique_ptr<>> works.
- Move smart containers to their own header.
I was learning from @jdelong and he said that you should use
double quotes for local includes and angle brackets for library
includes. I asked why our code was the way it was, and he said he wanted
to clean it up. I beat him to it :)
Conflicts:
hphp/runtime/base/server/admin_request_handler.cpp
hphp/runtime/vm/named_entity.h
Add support for external thread events.
See asio_external_thread_event.h for details about usage.
This is the first version that compiled, the code was not executed even
once, but wanted to get it out early to unblock @hannesr. Thus the [RFC]
flag.
My next step is to implement asio-compatible equivalent of
fb_call_user_func_async() that will make it possible to easily test the
new functonality by simulating async stuff with sleep(), etc. on the PHP
side.
There are 2 major mostly lockless synchronization points that needs
careful review:
- queue of ready external thread events (asio_session.{cpp.h})
- shutdown cleanup (asio_external_thread_event.{cpp,h})
All m_* object members in ext_asio are camelCase, except in AsioContext.
Rename them. Also, m_queue_ready became m_runnableQueue, as with the
introduction of async functions a new abstract RunnableWaitHandle will
be introduced.
If an internal HPHP exception is thrown in a continuation executed by
ext_asio, m_current pointer was not reset and resources were not cleaned
up. This doesn't matter that much in prod, but when used in debug mode,
an assertion was hit.
Currently, we detect dependency loops by waiting until there is nothing
else to execute. If the wait handle we are waiting for did not finish,
it means it is in a cycle. We find the cycle by simply following the
dependency chain. Once the cycle is found, one edge is eliminated and an
exception is injected.
There are multiple problems with this approach:
1. Unability to exit contet safely
We are unable to exit context safely. When a context is exited, all wait
handles in that context must be kicked out. But we maintain only
references to the SCHEDULED wait handles + BLOCKED wait handles that
recursively depend on them.
If we do not kick out all unfinished wait handles, we end up in
corrupted state.
2. Unability to break edge that caused the cycle
Once the cycle is detected, we don't know which edge caused the cycle to
be formed. We can only use heuristics to eliminate the edge that likely
formed the cycle, we cannot be sure. This may make it very hard to fix
the PHP code that caused the cycle.
Solution:
This diff implements online cycle detection with a naive approach of
visiting the dependency chain from child at a time new edge between
parent and child is being added. If a parent is visited, a cycle is
found. Otherwise we eventually reach non-BLOCKED wait handle as it is
guaranteed the rest of the graph is cycle-free.
Currently, wait handles store pointer to the context they are in. This
pointer is not protected with reference counting, as it is expected that
whenever a context is exited, references to it are cleaned thru
exitContext() mechanism.
If a bug is present that violates this assumption, it is impossible to
guard against invalid pointer access and a hard to debug memory
corruption occurs.
Since the structure of contexts is a simple stack, let's reference them
by index instead of by pointer.
As a bonus, one pointer worth of memory is saved for every non-trivial wait handle.
The actual bugs will be fixed by the next 2 diffs that do:
1. implement online cycle detection
2. do enterContext() atomically and properly handle failure
And use them in AsioContext, which was doing a lot of memory
allocation via malloc/free, was itself allocated by malloc, and
needed to be sweepable to deal with the fact that it contained
standard containers.
This change is mostly for FB internal organizational reasons.
Building is not effected beyond the fact that the target now
lands in hphp/hhvm/hhvm rather than src/hhvm/hhvm.