-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
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
module deinitialization paradigms, cross-platform termination detection #9388
Comments
I'm afraid this is mixing two inherently different things. But this has nothing to do with modules (module is a compile-time concept - anything runtime is not handled by V AFAIK and thus is handled explicitly by the programmer - imagine something like manual DLL loading using winapi). Remember, V modules are statically compiled, so there is no point in time when a module is "not reachable" or "unloaded" etc. So could you clarify what's the use case and the desired semantics of a potential trigger running a As a side note, I'd discourage anyone using |
I gave
The goal as I understand is to continually eliminate C from V code. |
Yes. There should be "pure" V implementations of everything. If that gets translated to C.atexit by cgen, that's fine, but it should not be the only implementation. Otherwise you are throwing roadblocks to other backends, such as x64, JS, WASM, etc., etc. |
I might have not been clear enough - sorry for that. My comment wasn't about When exactly should
|
Safety would be guaranteed in the usual way. For example, you might have the following for explicit manual memory management: module my_module
[manualfree]
fn init() { /* ... */ }
[manualfree]
fn deinit() { /* ... */ } Though you could use them, of course, without manual memory management. |
@Ben-Fields I'm afraid I still do not understand anything about the "safety" measure(s) you're proposing. Let's put it this way - a module's |
@dumblob From the docs:
You're right, maybe it won't be terribly useful beyond cleaning up C @larpon Could you share your use-case for |
Yes. My specific case is a C library I'm currently wrapping. The C library provides it's lifetime management via 2 methods (like many many other C libs do) it has an Since V has a module init, that part was easy to adapt - but placing the After working with it I'm still unsure how to solve it properly. If the module get included in a big project involving several independent vendors that happen to include the module twice having a module Wrapping it in a struct with a new/free cycle also is problematic since the C lib is only build for monolthic use (as far as I can see). So it's not really easy to put into a V "life-cycle" scheme. You could call it a kind of "singleton module" In the end it'll probably just be shipped with a warning that tells you to only use one instance at every point in time - but currently my best option is the module level init() combined with C.atexit and os.signal() to cover the most cases I think. I agree with @dumblob here that there's no reliable way to know when to shut things down for certain in all usecases. I my case a module deinit function should fire only once and when execution terminates in some way - be it by Ctrl+C or "normal" exit from main just before it returns 0 to the calling shell. I'm not sure about process suspension/continuing - in my case it's not using a ton of memory - so I probably won't deinit -> init again in this case |
Hmm... maybe a module deinit with a way to specify at what points deinit should be called ? |
Sounds like infinite dependency hell to me... But how about approaching this whole thing differently? Namely find a canonical way to make singleton across processes, then a singleton across os threads and finally a singleton across go routines. If this will be readily available, then calling manually any |
Yeah, maybe 🤷♂️ |
Just to clarify for further discussion - I meant to have all 3 (independent from other) singleton kinds available in V. Not just one of them (and thus only one |
If you allow multiple instantiations and freeing of a I think this diverges from my original post; I was misleading bringing up "lifetime" I suppose. My thinking was that Just as If you want to |
Yes singletons are user controlled, agreed. it's a V version of |
Here I'll again point out, that I was talking about different lifetimes - and there are exactly 3 language-level lifetimes in V if I'm not mistaken - so the How this will be enforced is another question which I wasn't discussing 😉.
I think safety here is enormously important - and saying "useful because modules that are not the main module do not have access to
This won't work with JS backend nor with bare metal "backend" and I thought we agreed above we actually need a crossplatform solution. For for now it should be enough, that's for sure 😉. |
To be clear, I completely agree with @dumblob 's first post that modules are a compile-time concept and do not have a lifetime. I am backtracking my opinion on the initial " With regards to
I'm unfamiliar with vweb, but ability to register js events would be required (ex. Some
I'm going to slightly disagree on this one in that I more highly value simplicity and clarity than I do enforcement of 'safety' abstractions. I think the former, when the two are at odds, will produce better code (which is why V is an attractive language; though yes, it tries to do both). For the provided use-case, either define a custom module init/deinit (vaguely similar to beginning/ending a graphics context) with Many of the standard modules are wrappers for C libraries. To gain safety, among other things, V gives up some power where raw data management is concerned, and to gain some of that power back in certain situations, it has great C interoperability, where you must (re-)compromise on safety. As a side note, as I understand, a similar adherence to simplicity and clarity is why "auto (de)referencing is going away". I think the above compromise is better than ensuring the module is initialized for the entire program lifetime, which may not be desired, or until the world is written in V, and we can cast aside distasteful 'unsafe' code dependencies. Misc.
Modules are a static container for functionality. If it were solely up to me (which it's not), I would remove
TL;DR: Don't overcomplicate. Implement some |
@Ben-Fields I think I understand. But could you sum up how it would look like in practice? I actually see 2 different mutually orthogonal use cases.
If both (1) and (2) get implemented, then nothing stops you to prepare some thread-locking in |
This exactly. There is locality, it's similar to file resource management, and documentation is hugely important. With existing V, it's simple, and is this not already possible? This is where I think the role of the "module creator" ends, and the "module user" has responsibility to use the module correctly. As such, I am against introducing measures such as to "enforce calling the closure callable" (agree that there are more cons that pros). In Larpon's situation, there is some global state. I maintain that I would use something similar to the dismissed struct method / what is quoted above, giving power/responsibility to the module user. The global state case is a rare case (globals are discouraged in V). Importing two modules that both use the global state module as a dependency is an even rarer case, so perhaps simply keep an initialization flag to ensure the initialization / de-initialization only happens once. Also, in situations I can envision, I do not think a module should hook into init() or atexit() (minimize 'hidden execution'). The module user has responsibility to call them / place them in the right hooks. Separately from the discussion of general initialization and de-initialization, I do think there needs to be an atexit/abort/whatever in pure V: fn main() {
on_abort( my_cleanup_callback_1( /* ... */ ) ) // part of builtin, causes warning / prod error outside of
// main module, some way to specify context/data
// ...
}
fn my_cleanup_callback_1( /* ... */ ) {
// Unexpected or asynchronous program end.
my_cleanup( /* ... */ )
output_to_log( /* ... */ )
ensure_user_data_saved( /* ... */ )
module1.cleanup( /*... */ )
module2.end( /* ... */ )
}
|
Very well, then we're on the same boat 😉.
Unfortunately not - closures are not yet implemented if I'm not mistaken. So these rather important programming patterns did (could) not emerge yet 😢.
This is actually more complex than it seems from your example and quick description. Generally it boils down to In other words without |
@dumblob I appreciate the info, shared issues and your role in them. I see that this issue needs to be revisited when several parts of the language are refined. |
deinit()
I'd like to not see The module could provide a function for deinitialization that the importer calls when they are finished with the module. This is much clearer for the importer, easier for readers to understand, and arguably tiny overhead for the importer. It's one function call. This provides much more flexibility because the importer may want to deinitialize the module before the program is finished. This is also discussed in #7610. |
@leighmcculloch Agree completely. There are just a few cases where "a function for deinitialization" cannot be provided due to V's strict scoping. Initial post did not reflect discussion; updated. |
@Ben-Fields Could you share a code example of where a function for deinitialization couldn't be used? |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
deinit()
. Similar toinit()
, but called at the end of the module's lifetime. (also similar tofree()
for structs).This can be accomplished using C code currently -C.atext()
(int atexit(void (*func)(void))
).C.atexit()
exists, but with scoping & avoiding globals, the relevant context(s) cannot be accessed. Request more robust cross-platform termination detection, and a potential similar alternative toinit()
, so that meta/system-level control flow can be properly handled by the main module.Discussion TL;DR: Current methods not cross-platform (
C.atexit()
,os.signal()
), encourage wasting resources (init()
), encourage sub-modules to alter system-level control flow (os.signal()
,exit()
,panic()
). Pattern with closures proposed.Sub-issue of sub-modules having globals and side-effects. Globals are a necessity for C interop and low-level (effectively discouraged for general use), but side-effects (making globals apparent to the main module, or altering system-level control flow) can and should be avoided, at least not encouraged.
The text was updated successfully, but these errors were encountered: