-
Notifications
You must be signed in to change notification settings - Fork 769
[SYCL] Load PI plugins only once #1614
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -255,8 +255,19 @@ bool trace(TraceLevel Level) { | |
} | ||
|
||
// Initializes all available Plugins. | ||
vector_class<plugin> initialize() { | ||
vector_class<plugin> Plugins; | ||
// The returned reference lifetime is through the end of the process. | ||
// | ||
// TODO: make sure that the global Plugins is destroyed last, at least | ||
// after other tear-down relying on plugins (like destructors of global | ||
// buffers) is completed. | ||
// | ||
const vector_class<plugin> &initialize() { | ||
static bool PluginsInitDone = false; | ||
static vector_class<plugin> Plugins; | ||
if (PluginsInitDone) { | ||
return Plugins; | ||
} | ||
smaslov-intel marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems it is possible for two threads to reach this point, so both accesses There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? Plugins are really read-only immutable objects after they are constructed in pi::initilize(). We should probably make it explicitly such, so that people aren't able to write it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If two threads create platforms for the first time simultaneously they can reach this point or I missing something? |
||
|
||
vector_class<std::pair<std::string, backend>> PluginNames; | ||
findPlugins(PluginNames); | ||
|
||
|
@@ -310,6 +321,7 @@ vector_class<plugin> initialize() { | |
<< "Plugin found and successfully loaded: " | ||
<< PluginNames[I].first << std::endl; | ||
} | ||
PluginsInitDone = true; | ||
|
||
#ifdef XPTI_ENABLE_INSTRUMENTATION | ||
if (!(xptiTraceEnabled() && !XPTIInitDone)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Who owns plugin? I thought it was platform_impl::MPlatform.
Related question:
What is the lifetime of Plugins? What happens if user creates a platform(to enter this function) from a global object destructor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
plugins are system-wide, think of them as a global list of shared libraries loaded. it is currently created at the first call to pi::initialize() which I think (TODO) we should fine a better place for than in platforms_get(), and also (TODO) deallocate them at global tear-down.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still, who owns plugins right now? I see that platform_impl has
std::shared_ptr<plugin> MPlugin;
field, so I expect that platform_impl objects own plugins. When all platform_impl objects are destroyed plugin object will be deallocated as well, soPlugins
vector will point to already released memory.What happens if user creates a platform(to enter this function) from a global object destructor? For example the following SYCL application can(it's UB) catch a crash with your implementation:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The question about the lifetime of the
Plugins
vector is interesting. I looked at the SYCL spec, and I don't see any restriction that prevents a user from calling SYCL APIs from global destructors, so I think we need to assume that code like @romanovvlad shows above is legal.This probably means that it is generally unsafe to use any static storage or namespace scope C++ objects anywhere inside the SYCL runtime. Consider the
static vector_class<plugin> Plugins
added by this PR. ThePlugins
vector will be destroyed sometime after the application'smain()
function returns (or whenexit()
is called). However, a global destructor in the application could call SYCL APIs even after that. If such a call happens afterPlugins
is destroyed, the application will likely crash.One way to solve this is to define a static pointer to
vector_class
instead. Theinitialize()
function can callnew
to allocate the vector, with no matchingdelete
. This means the memory will never be deallocated, but this is necessary if you want to support SYCL API calls even from global destructors. Of course, the problem might be wider than that this particular PR. There might be other code that defines static storage or namespace scope objects.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also agree there is a problem caused by undefined order of global objects destruction. The pointer solution is an interesting one (especially for its simplicity), an is probably a suitable one where destructors have no important side effects.
An alternative would be to put some order to the objects destruction (there are C++ extensions to do so, e.g. GNU attribute "destructor" with priority: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html). Vlad mentioned there is already some llvm class like GlobalGlobal (not remembering the exact name), that are guaranteed to be destroyed after other globals.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer that we do not use global objects.
If it's really needed I would suggest having a global pointer solution which is deallocated in "GNU attribute "destructor"".
Are you sure "plugins" have no important side effects? Shouldn't they at least dlclose a plugin library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we work on it separately from this change?
I added a TODO explaining that we need to destruct plugins last of all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the
dlclose
is necessary. Remember, this is happening right before the process terminates. The library will get implicitly dlclosed when the process termintes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
User can load libsycl.so using dlopen, pugins libraries will not be properly freed by dlclose.