-
-
Notifications
You must be signed in to change notification settings - Fork 604
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make symbols visible to the object itself from threads created by INIT functions #1067
Comments
I'm not convinced the "self lookup" flag will work in this case: If I understood that self-lookup flag trick, you use it when an object is relocating its own symbols, and it knows it is doing that. However, when the init function is being run, it just runs ordinary code and then calls something which needs resolving, and it doesn't really know this is a "self lookup" (unless I'm misunderstanding something). How about the following idea? It might work, but then again I'm not sure I fully understod the use-case of the init function being run in a different thread: We can have a per-thread variable saying that this thread is currently running an init function for object X: __thread object* initializing_object = nullptr; We set this to the current object when running the init function, and then reset it back to nullptr. We also write code so that when a new thread is created, this variable is copied from its parent (I have a patch for this feature, which I called INHERITED_THREAD_LOCAL_SIMPLE). Then we change the definition of bool object::visible(void) const
{
auto v = _visibility.load(std::memory_order_acquire);
return (v == nullptr) || (v == sched::thread::current()) || (initializing_object == this);
} (the new part is just the addition of the I can think of another way that doesn't involve adding INHERITED_THREAD_LOCAL_SIMPLE (which although I have the patches already, isn't trivial): We could add to OSv the notion of a "parent thread" (should be easy). bool object::visible(sched::thread *me = sched::thread::current()) const
{
auto v = _visibility.load(std::memory_order_acquire);
return (v == nullptr) || (v == me) || (me->parent() && visible(me->parent()));
} The trick here is that if the object is not visible for this thread, we check (recursively) maybe it's visible to its parent thread - until we reach the root whose parent is null. In the usual case, visible() will, just like before, quickly return true. Only in the rare case, while loading an object and attempting to access a yet-hidden symbol, will this recursion use any non-trivial amount of computation. |
The "self-lookup" flag is intended to be ON only for the cases where symbol is being looked using its index in the symbol table rather its name. Specifically these cases involve only calls to
This flag was originally intended to deal with symbols "deprecated" by versioning table which should be still visible to the object itself (see #1066) for example when the INIT functions are called. I thought same flag could also be used to deal with the "threads/INIT" issue. But I think you are right that it is not quite correct. I must say I like your INHERITED_THREAD_LOCAL_SIMPLE proposal (could you send a patch?). It would be nice to see how The code around INIT looks like this now: //
// After loading the object and all its needed objects, run these objects'
// init functions in reverse order (so those of deepest needed object runs
// first) and finally make the loaded objects visible in search order.
auto size = loaded_objects.size();
for (unsigned i = 0; i < size; i++) {
loaded_objects[i]->setprivate(true);
}
for (int i = size - 1; i >= 0; i--) {
loaded_objects[i]->run_init_funcs(argc, argv);
}
for (unsigned i = 0; i < size; i++) {
loaded_objects[i]->setprivate(false);
}
_loaded_objects_stack.pop(); shouldn't the latter part of it change to this: for (int i = size - 1; i >= 0; i--) {
loaded_objects[i]->run_init_funcs(argc, argv);
loaded_objects[i]->setprivate(false);
} to make the object visible right away after its init functions are called. Imagine an init function in object A that depends on B needs resolving a symbol in B, in same thread or another created by init function? Now I am not sure how this would affect the INHERITED_THREAD_LOCAL_SIMPLE approach. Now regarding the parent thread idea, I actually had same one but I also thought it would be more complicated and less efficient because of the recursion (what a coincidence :-)). But I just realised that having the concept of parent thread id could be extremely useful for debugging purposes or possibly others (imagine tracking which thread created another one, etc) regardless if we use it to solve this problem. So if you have patch just for that we should apply it. |
The problem we are trying to solve manifests itself for example when one tries to run numpy with python on OSv (see these mail group discussions) and it fails to resolve one of the symbols:
One of these example succeeds if OSv runs with single vCPU. Most likely OpenBLAS tries to create a thread pool in one of the init functions and fails resolving |
Regarding the parent thread approach, I think we can avoid recursion by using a simple loop:
But I think we have a bigger problem: what if parent thread terminates before the child does? Maybe we can deal with it by using some kind of indirection - "proxy" object that both child and parent will point to and parent will deactivate in its destructor:
Making parent keep track of its children and notifying the when destructed seems like a nightmare, |
The commit fe37650 to address the issue #1067 relaxed the elf visibility rules while being loaded to child threads so that they can resolve symbols if any threads are created by the INIT functions. Unfortunately inadvertently that patch allowed access to elf ebjects too early in some cases and would lead to a crash. Here is a concrete scenario when this would happen with the 'cli' app: 1. Thread A resolves objects of the httpserver app. 2. Thread A starts the httpserver app on new thread B. 3. Thread A continues to load objects of the cli app. 4. Thread B tries to resolve some symbols through elf_resolve_pltgot() for example and ends up attempting to look up symbols of cli object that is just being loaded by thread A and is not "ready" yet. However because thread B is a child of A it can do so. Clearly the example above illustrates a hole in the original patch. This patch fills this hole by restricting back the access to the symbols of the object being loaded to the thread doing it only as it used to be be. And it only relaxes the access to child threads for the brief time while object is being initialized. It does so by introducing the elf access visibility levels: - Public - ThreadOnly - ThreadAndItsChildren The Public and ThreadOnly and equivalent to the old - nullptr, non-nullptr visibility attribute. The ThreadAndItsChildren is only set to for the period the INIT functions are being called. References #1067 Signed-off-by: Waldemar Kozaczuk <jwkozaczuk@gmail.com> Message-Id: <20200111180020.22335-1-jwkozaczuk@gmail.com>
The original patch restricted access to symbols in a specific object until the object is fully loaded and initialized which includes running the INIT functions. It accomplished it by introducing simple visibility attribute that tracks which thread if any or all is supposed to be able to look up symbols in a given object.
Another patch which delayed calling INIT functions for application objects and its dependencies to a new thread so that these functions could access proper TLS variables if needed.
It turns out that sometimes the INIT functions may start new threads on their own and then any lookup by name (by
elf_resolve_pltgot
for example) of the symbols in the same object will fail as the new thread id will NOT match the thread id set when callingelf::setprivate
before calling INIT functions. So we somehow need to loosen this restriction and allow lookups if symbols looked by the object itself (self-lookup). It could be accomplished in very similar way as we do it for versioning table (see https://groups.google.com/forum/#!msg/osv-dev/d56plMGXi6E/Jm9lgv7xCwAJ).The text was updated successfully, but these errors were encountered: