This issue is about porting Signal
to Windows and therefore making it possible to implement other relevant features like Process
.
Microsft doc about signals
I already looked at this page, and I think these signals are only there because they are part of the C standard library. From wikipedia:
The C standard defines only 6 signals. They are all defined in signal.h header (csignal header in C++):
SIGABRT - "abort", abnormal termination.
SIGFPE - floating point exception.
SIGILL - "illegal", invalid instruction.
SIGINT - "interrupt", interactive attention request sent to the program.
SIGSEGV - "segmentation violation", invalid memory access.
SIGTERM - "terminate", termination request sent to the program.Additional signals may be specified in the signal.h header by the implementation. For example, Unix and Unix-like operating systems (such as Linux) define more than 15 additional signals; see Unix signal.
And these 6 signals are the only ones "supported" by Windows.
There is already an issue about this topic on the Node.js repo.
sam-github commented there:
[...] Windows as an O/S has absolutely no signal support. [...]
If SIGTERM was supported on Windows, you could listen for it and receive it... but you cannot. You can listen..., but you'll never receive it.
And I guess this is exactly the case. You have a function for listening to signals (signal()
), but there is nothing like kill()
for sending signals on Windows.
So this is about finding replacement functions to "fake" signals on Windows. I already found some replacements for signals one might want to send:
SendMessage(WM_CLOSE)
<- this is also what taskkill.exe
uses; works for windowed processesTerminateProcess()
<- used by taskkill.exe /f
; works for windowed and console processesGenerateConsoleCtrlEvent()
<- works for console processes, simulates Ctrl + CList of UNIX signals: https://en.wikipedia.org/wiki/Signal_(IPC)
Another microsoft doc that mentions replacements for UNIX signals: https://docs.microsoft.com/en-us/previous-versions/ms811896(v=msdn.10)#signals-and-signal-handling
Possible replacements for SIGSTOP and SIGCONT:
DebugActiveProcess()
(is actually for enabling a debugger to attach to the process, but also stops the process)DebugActiveProcessStop()
(same thing as above; resumes the process)Bear in mind, WM_CLOSE
is a windows message and thus does not work for console applications as far as I know.
Good news: WM_CLOSE, TerminateProcess and DebugActiveProcess work like a charm. I wrote a simple C++ console app for testing purposes: https://github.com/maltevoos/SignalFaker
Bad news: GenerateConsoleCtrlEvent doesn't work at all. It even says in the docs:
Sends a specified signal to a console process group that shares the console associated with the calling process.
Probably should have looked at this closer.
@sancarn I know this only works for windowed processes, but considering that it works very well for those and there is no real alternative that works for any process (as far as I know) I think this is the way to go. Of course we need to make it clear in the docs that SIGTERM doesn't work for console processes on Windows. Or we could call our SIGINT replacement instead when the user tries to send a SIGTERM to a console app.
@maltevoos If so then you might consider using WM_QUIT
. I personally don't know the difference between most of these signals to be honest (not a native linux user... 😛 )
@sancarn So what exactly is the difference between WM_CLOSE and WM_QUIT and what makes one better? Btw, a short explanation of the signals I mentioned:
WM_CLOSE
is sent to the window when the "X" is pressed on a window.
Turns out that I was mistaken and WM_QUIT
is not related to the window. The message indicates that a thread should quit. But it is only called from within the thread itself using PostQuitMessage()
. It's used to tell windows that the Message loop should be stopped. It doesn't look like you can easily quit a remotely running thread with it at least.
I think we should stick with WM_CLOSE because this is what taskkill uses (at least multiple stackoverflow posts say so).
Also I can confirm that WM_QUIT does nothing when sending it to a window.
Are you sure GenerateConsoleCtrlEvent()
doesn't work?
// It's impossible to be attached to 2 consoles at the same time,
// so release the current one.
FreeConsole();
// This does not require the console window to be visible.
if (AttachConsole(pid))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
// Must wait here. If we don't and re-enable Ctrl-C
// handling below too fast, we might terminate ourselves.
Thread.Sleep(2000);
FreeConsole();
// Re-enable Ctrl-C handling or any subsequently started
// programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
or for the current process use AllocConsole
instead of AttachConsole
.
Edit: Also found an article about it which provides full example.
https://github.com/jruby/jruby/blob/c3385b8feac7c05675a5f430b0d9b79f0edc5d37/core/src/main/java/org/jruby/RubyProcess.java#L1286 is how jruby does it, well for 0 and 9 anyway.
0 liveness 1 HUP (hang up) 2 INT (interrupt) 3 QUIT (quit) 6 ABRT (abort) 9 KILL (non-catchable, non-ignorable kill) 14 ALRM (alarm clock) 15 TERM (software termination signal)
And here's how ruby does it https://github.com/ruby/ruby/blob/6a65f2b1e479f268489b51a004b6c153c634c68a/win32/win32.c#L4881 0,2,9
Maybe 15 could be WM_CLOSE if it's windowed, else OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, (DWORD)pid);
?
FWIW :)
@sancarn That looks pretty promising indeed. Will try it out later today :)
@rdp OpenProcess
just returns a handle to the process object with the given access rights so that would be suitable to check whether a process exists (signal 0).
We already have 0 and 9 anyway. But do you understand ruby's implementation for SIGINT?
Edit: Apparently the PID != 0
check Ruby uses ensures that when a PID is specified a Ctrl-Break event is generated instead of a Ctrl-C event. It says so in the comment:
/* CTRL+C signal cannot be generated for process groups.
* Instead, we use CTRL+BREAK signal. */
But AFAIK the Ctrl-C event should work just fine in any case so I'll try that first.
My hunch is they always send ctrl+break so that it works for "both pids and process groups" in case they happen to be targeting a process group? Kind of weird...
Also, appears one needs to call AttachConsole before sending the ctrl+break but...then you lose your current console, not to mention temporarily losing your own ctrl+c handler (see "cons" listed here https://stackoverflow.com/questions/813086/can-i-send-a-ctrl-c-sigint-to-an-application-on-windows ). Maybe we should just shell out to taskkill ... ? :/
Also, appears one needs to call AttachConsole before sending the ctrl+break but...then you lose your current console, not to mention temporarily losing your own ctrl+c handler
@rdp See https://github.com/crystal-lang/crystal/issues/7339#issuecomment-458331518
Maybe we should just shell out to taskkill ... ? :/
@rdp taskkill
uses the window message WM_CLOSE
so that won't work for console processes. If I try to kill a console process like node.exe
it prints out that the process could not be terminated and I have to use taskkill /f /im node.exe
. That on the other hand is not what we're looking for because it abruptly terminates the process like SIGKILL
.
@sancarn I think one of the "cons" is that after running code like that you would no longer be bound to your initial console (as in...printf would now go no where...)? @maltevoos dang...wonder if there is any built-in way to "shell out" to something that sends a ctrl+c signal? I couldn't find any...I guess we could tell people "if you want this to work, you have to bundle it with this helper exe" :|
Unrelated to the standard linux signals, I wonder if there's a way to add handlers for other windows signals, like CTRL_LOGOFF_EVENT https://docs.microsoft.com/en-us/windows/console/handlerroutine or maybe it's already possible dunno. Might be useful long term anyway, FWIW :)
@rdp I agree. We can't accept losing our console window just for terminating another process. Additionally, GenerateConsoleCtrlEvent doesn't do the same as a real Ctrl-C user input. For example when Ctrl-C-ing node, it always prompts the user to press Ctrl-C again. When using GenerateConsoleCtrlEvent, it prints out "^C", but node stops immediately.
I meant to comment on this issue a few days ago, but got bogged down. I had been looking into this because I wanted to help port Signal
. I am by no means an expert on this stuff, so if I get something wrong or I'm way off base here, please let me know. Most of the stuff mentioned above should work, so I feel like we're on the right track. We can use abort()
for SIGABORT
.
My one suggestion is that we provide support only for the the signals that Windows itself "supports" based on the latest docs.
SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, SIGBREAK, CTRL_C_EVENT
and CTRL_BREAK_EVENT
.
Windows uses Structured Exception Handling, and so we can't really catch stuff like SIGFPE and SIGSEGV unless we wrap main
in the __try__
/__except__
syntax and then create a handler to raise the correct POSIX signal from the corresponding Windows exception. There's also Vectored Exception Handling which would allow us to just setup handlers, but it's not available on 64-bit Windows. Maybe those are outside of the purview of Signal
but they aren't handled right now on Windows. This book has some more information on that.
If we take the route of only supporting the above signals, we can be very clear about this in the documentation and set expectations early. The Windows story for signals across newer languages seems shaky. It isn't clear what exactly Go implements for Windows. It seems to only implement the CTRL-C stuff. It does not implement SIGINT
for Windows.
Rust doesn't seem to implement anything for Windows in the standard library. The tutorial suggests using a 3rd party library, and there's an RFC to add signal handling to the stdlib, but it's still open, and the latest reply suggests using a 3rd party library.
Python handles things like I've mentioned above (and this seems like the sanest approach). It's much less work than trying to find replacements for all the UNIX signals, especially when we can't necessarily guarantee that they'll always behave the same way (through changing system APIs, etc.) It's less maintenance going forward, and I'd argue that the Python signal handling presents a more concise and coherent API surface for someone using the language.
Edit: Wording.
@markrjr Catching signals on Windows works the same way as on UNIX, namely through signal()
. Our main problem is sending signals to other processes. On UNIX we have kill()
where we just need to specify the process id and the signal we want to send. On Windows, such a thing doesn't exist (at least from my understanding) so we need to find replacements. Apart from that Windows doesn't seem to use its signals that much, e.g. when closing a window, there is a windows-specific window message being sent rather than a SIGTERM signal which makes listening to signals quite pointless (see first post).
Edit: Apparently Windows does use the signals it supports (like SIGINT or SIGABRT). Looks like I was mistaken there.
@markrjr The Python devs only did the easy part, namely registering signal handlers for the signals supported by windows when the user calls signal.signal()
. When it comes to sending signals (what this issue is mainly about), Pythons implementation is rather poor:
Send signal sig to the process pid. Constants for the specific signals available on the host platform are defined in the signal module.
Windows: The signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT signals are special signals which can only be sent to console processes which share a common console window, e.g., some subprocesses. Any other value for sig will cause the process to be unconditionally killed by the TerminateProcess API, and the exit code will be set to sig. The Windows version of kill() additionally takes process handles to be killed.
Why Python's way of doing this is bad:
signal.CTRL_C_EVENT
only works when the target process and the calling process share the same console.TerminateProcess()
. Even if you send something like SIGCONT
(which is for resuming a suspended process), the target process will be unconditionally killed. It's like every signal was replaced by SIGKILL. This is just confusing for people working with Python.os.kill
takes a HANDLE
to the process, which is unnecessarilySadly, most languages with signal "support" on Windows do it this way. Crystal should do better 🥇
@rdp > I think one of the "cons" is that after running code like that you would no longer be bound to your initial console (as in...printf would now go no where...)?
I don't think that is true, because I assume Crystal will implement echo
etc by writing to the STDOUT
pipe directly but may be mistaken. But even if it were, you can always call AllocConsole
afterwards to re-join the existing processes console.
@sancarn I tested it yesterday. It doesn't work.
Edit: Maybe I missed the AllocConsole()
. I'll try it again and if it works, we could see how low we can go with the Sleep
duration.
Edit 2: I tested it again with AllocConsole but this opens an entirely new console window which looks very weird. Another approach that came to my mind:
GenerateConsoleCtrlEvent
AttachConsole
with the pid of the dummy processAnother nice thing about this would be that we probably don't need a Sleep
call.
I didn't read the entire conversation, but in my opinion signals in Crystal should be done like in Go: only SIGKILL
is supported in Windows, trying to use SIGINT
gives an error in Windows, and other signals are only available in Unix.
References:
@asterite I disagree. In my opinion we should support any signal that can be elegantly replaced with a WinApi call.
Currently we have:
SendMessage(WM_CLOSE)
for GUI processes, TerminateProcess
for console processesDebugActiveProcess
DebugActiveProcessStop
TerminateProcess
The functions above are tested and (almost) work like real signals on UNIX.
Why should we only support SIGKILL/TerminateProcess
if we have good replacements for other signals too? I would be fine with just supporting the four signals above since these are the ones you would really want to use.
@maltevoos There's an argument about maintenance overhead in supporting these replacements - how big would it be (LOC-wise), what's the complexity of OS-support matrix, how many edge-cases (like SIGTERM
) we need to handle - all in all, is it worth it?
I'd go for sure for all of the cases we can support transparently, and weighted the rest against above argument.
@Sija IMO it is worth it:
(lines of naive C++ code, each signal only takes a PID like on UNIX)
See https://github.com/maltevoos/SignalFaker
Probably there is some more work to do if we want good error handling etc.
I misunderstood, the blurb about structured exception handling is only important with OS -> Process communication, but since we're looking for IPC then we'd probably want to use the Messages API.
@maltevoos It looks like you've already experimented with the Messages API for SIGTERM
by sending the WM_CLOSE
message. We could send arbitrary messages for the other signals (like SIGILL
or SIGFPE
and once received, raise the offending signal. We would want to post them to the thread. See PostThreadMessageW. Those messages then would only be understood by other Crystal programs, though. That's likely why Python developers just decided to use TerminateProcess for everything.
We could possibly use feraiseexcept
for SIGFPE
.
@markrjr I don't think we should invest time into this. I mean, what's the point of having two Crystal processes (that must be GUI processes) running at the same time and communicating to each other? We should keep focusing on sending signals to external processes.
It's not much work/overhead, and makes it so you don't have to remember/use a "different way to kill process based on the current OS" as it were...I like maltevoos' roadmap (though who uses SIGCONT etc. in real life, but maybe still useful). I believe you can catch SIGFPE etc. but you can't "send" it to another process AFAICT.
@rdp True, after more consideration I think the work here @maltevoos has done is the best approach and there's already a good chunk of progress.
Having a quick look at signal.cr I see Mutex
used at some points... Does that mean we have to port Mutex
first before continuing here?
I think we should be alright, Mutex
doesn't look OS dependent. @ysbaddaden would probably have the definitive answer.
@markrjr Just wondering because it's on the to-do list in #5430.
Signals are more complex than they seem on POSIX platforms; writing signal-safe code in signal handlers is tricky, which is why we merely write the signal to a pipe and later handle 'em through the event loop, not even talking about its implications to fork/exec (fork inherits sigmask).
TBH there are lots of things to work on to support win32; I'm not sure signals has much priority, and even thought maybe a correct implementation could depend on Fiber, EventLoop, Mutex... maybe something else, or maybe not. I don't know anything about win32.
@ysbaddaden Exactly. Fibers and IO are far more important to implement for win32 because a lot more depends on them.
I wouldn't do that at all. Signals are not just a concept of async inter-process messaging, it's a concrete and very well defined mechanism that people have been using for ages. It would be extremely conter-intuitive for the users to use something that is called a Signal, but has in fact nothing to do with posix signals.
Instead you would be much better off introducing a new abstraction that will wrap signals on posix systems and winapi on windows, however the utility of such a concept may be less than you think.
In general I think the goal of linux-windows cross-platform development looks noble at the first sight, but contains so much devils in the details that it harms users more than helps them.
Some kind of abstraction layer might make sense...you know like
Process.alive?(pid) or what have you (that kind of thing) :)
On Fri, Mar 8, 2019 at 7:26 AM undead robot notifications@github.com
wrote:
I wouldn't do that at all. Signals are not just a concept of async
inter-process messaging, it's a concrete and very well defined mechanism
that people have been using for ages. It would be extremely
conter-intuitive for the users to use something that is called a Signal,
but has in fact nothing to do with posix signals.Instead you would be much better off introducing a new abstraction that
will wrap signals on posix systems and winapi on windows, however the
utility of such a concept may be less than you think.In general I think the goal of linux-windows cross-platform development
looks noble at the first sight, but contains so much devils in the details
that it harms users more than helps them.—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/crystal-lang/crystal/issues/7339#issuecomment-470945763,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAAw0J0Wim7xazYayWXnR5O96Wrx-xFTks5vUnL2gaJpZM4aU0lE
.
Should we perhaps have a UNIX
module in the standard library, move signals to UNIX::Signal
and then make it very clear that the UNIX
module just will not be there on windows?
Obviously, we'd have cross-platform abstractions for the most common usecases (exit handlers, killing a process, etc.)
I think platform-dependent stuff should be in shards, not in the std. Otherwise a crystal program without external dependencies can't be reliably compiled on any platform (I experiences this in Haskell where there are modules named Posix
and such, it looks very messy in my opinion and a big leaky abstraction).
Moving Signal out of stdlib is going to be hard. We need 'em in stdlib on POSIX targets. For example:
Process::Status
, ...For this, we need the whole signal sink logic (sighandler -> IO pipe -> event loop -> fiber handler) to be implemented (signal handlers are unsafe) + setsigmask to disable signals during fork/exec when running sub processes (because of the sink).
Hiding signals inside a shard would mean to either replicate everything we already have in stdlib (bad), or merely expose whatever stdlib provides (sic).
AFAIK we don't need Signals on Win32 (thought there _are_ signals on Win32). I don't see any reason to introduce yet another namespace to contain it. It would be quite empty. We're like to dist fork
and won't introduce daemonize (use a specialized tool like supervisord). Go for example merely exposes a few syscalls like chroot
and a few methods to get/set the real/effective user/group (half of them being dummies returning -1 or erroring on Win32), and that's about it. No real reason to introduce a namespace for such very specific use cases.
Moving Signal out of stdlib is going to be hard.
Agreed that this isn't feasible.
Go seems to mostly stub signal to only handle SIGINT
, which is fine with me.
What should we do about the desire to have all possible values be available at compile time?
I.e. if we have Signal::PIPE
on Linux then it must also be available on Windows? But it cannot be used for anything
And in case it turns out relevant, I'd like to point out that there's 1 collision of values:
Windows SIGABRT = 22
, Linux SIGTTOU = 22
The values can be different on different OSes. I think that a good compromise is to have a commonly used set available, but their value is set to -1
on unsupported OSes and we just raise when they're passed anywhere to be used.
It's a tough call to put those values into enum Signal
specifically because it is an enum.
Errors could very easily sneak by undetected, if organized like this:
enum Signal
A = -1
B = -1
end
p! Signal.new(-1) # => A
If, instead, the real enum contained only actually defined signals but Signal
was a namespace with constants like PIPE = ActualSignal.new(-1)
, they would error upon first use.
Which is a bit silly still.
I think regardless of what the actual enum values are, Signal
should be an enum, should be identical on all architectures, and methods should raise only when an unsupported signal for that platform is used.
I suppose we should have our own set of enum values then, and translate them to the appropriate POSIX/Win32 values when used.
@straight-shoota how will .value
work?
Like with any other enum. We'd need a separate method to return the platform-specific signal number.
I think people will be really tempted to use Signal.value
anyway, any time they want to look at it or pass it to some unsupported low-level call.
What would signals even mean in Windows? Why not just make them unix-specific, like unix sockets?
I think the main point is that signals can't feasibly be dropped from standard library and we also have a policy that code that compiles on one platform will always compile on another. So there has to be some placeholder even if it doesn't actually work. So we're discussing what that would be.
signals can't feasibly be dropped from standard library
Could you elaborate on this point? Wouldn't this be solved by conditional compiling? I'm almost sure it's already in wide use, something akin to ifdef windows
As I was saying,
we also have a policy that code that compiles on one platform will always compile on another.
If I write process.signal(Signal::KILL)
and it works on Linux, it must also compile on Windows even if it's a runtime failure. Hence, Signal::KILL
must be an existing value of some sort.
Let me bring back my refrence to Unix sockets. There are basic fundamental differences between operating systems that will not go away. You may go C++ route and leave them "implementation specific" and out of stdlib, but that would be very unhelpful for the users.
If you insist on a universal higher-level API, then make Signal this implementation detail only available on some systems and wrap winapi for windows. Good luck designing this API though.
Basically I understand this policy as: "even if a thing only exists and makes sense in a particular case, we'll mock it up as a universal case so it's gonna explode in runtime rather than in compile time", am I correct?
The last point is correct. I don't like that policy myself but this is not the place to rethink it.
Can't we just do what Go does?
Well, what does Go do? Specifically in terms of the actual values of the signal constants
The info was not enough. "Supported" could mean anything. So I dug a bit myself.
So I think what Go does is declare that these 2 are the only signals that exist and if you use anything else, you need an OS-specific library.
enum Signal
Interrupt
Kill
The only signal values guaranteed to be present in the os package on all systems are os.Interrupt (send the process an interrupt) and os.Kill (force the process to exit).
AFAIK only signals that _exist_ on the target are defined. I.e. Signal's enum should only contain valid signals, not fake ones. On POSIX systems you get all signals, on Windows you have the few supported ones. Overall, only Interrupt and Kill are _guaranteed_ to be available. Using other signals isn't portable.
IMO this is a good compromise. It breaks the "code must compile on all targets" but using signals shouldn't be recommended, and usually not needed.
Overall, only Interrupt and Kill are guaranteed to be available.
Using other signals isn't portable.
So how do you portably use even these two? Is it possible to kill(pid, SIGKILL)
on Windows? Is it possible to trap(SIGINT)
?
For the record, the value SIGKILL doesn't exist anywhere in Windows API.
trap(SIGINT)
is possible but I'm not sure exactly what it does.
"Sending a signal" in general is not a thing on Windows.
As was stated previously:
If SIGTERM was supported on Windows, you could listen for it and receive it... but you cannot.
You can listen..., but you'll never receive it.
And I guess this is exactly the case. You have a function for listening to signals (signal()),
but there is nothing like kill() for sending signals on Windows.
So how do you portably use even these two?
That's already answered in the issue description. And the same thing as Go does: When win32 Process#signal
implementation receives SIGKILL it calls TerminateProcess.
SendMessage(WM_CLOSE)
for SIGTERM probably isn't worth implementing when it only works for windowed processes.
I'm not sure about SIGINT in Go it's not implemented, but a TODO in the code =)
So for a MVP I'd suggest to only implement custom handling of SIGKILL on windows for now.
So for a MVP I'd suggest to only implement custom handling of SIGKILL on windows for now.
That's the obvious part.
So then, what should the value of Signal::KILL be on Windows?
Why not introduce like Process.terminate()
rather than emulate one special posix signal with something that has nothing to do with signals?
We are indeed moving in that direction, so perhaps we wouldn't need to fake-implement signals, but this is not a universal solution.
(terminate
was recently added and now is being decoupled from signals, look for it in https://github.com/crystal-lang/crystal/pull/9035/files)
I really like what go does, in that it defines a list of signals, and all the signal values are always there on every OS, even if using them is an error. That go has the same guarantees that compiling always works on every OS and any failures happen at runtime is a really good sign.
But only providing 2 enum values is not feasible in crystal. It's a simple extension of go's approach to add more signal values than just two.
and all the signal values are always there on every OS, even if using them is an error
-- all two of them - Interrupt and Kill.
I'm not sure how commendable that is
@RX14 To use signals you must use values from the syscall
package, which only defines _some_ signals for each target.
For example, syscall.SIGHUP
is available for both GOOS=linux
and GOOS=windows
, but syscall.SIGTTOU
will only compile for linux targets. On windows you get a compilation error:
$ GOOS=linux bin/go build foo.go
$ GOOS=windows bin/go build foo.go
./foo.go:14:28: undefined: syscall.SIGTTOU
I.e. using signals easily breaks Go programs portability. It's a good demonstration that the "compiles everywhere" policy is meant as a goal that shouldn't be enforced blindly —let developers shoot themselves in some corners, but make it explicit.
In Crystal terminology, this means that there isn't a Signal
enum, and to use signals, you must use LibC constants. For example LibC::SIGTTOU
and handle portability, for example with {% if flag?(:unix) %}
wrappers (if you care about it). I'd be fine with this.
I'm fine with that general approach, since it's obvious that LibC
and Crystal::System
are unstable APIs. Putting the constants under LibC
or under Crystal::System
I don't mind either.
In fact I'd like to move LibC
under Crystal
...
Neither Crystal
nor Crystal::System
namespaces should be accessed outside of core/stdlib. Putting signal constants there... undermines the whole point of these namespaces.
Putting LibC is also nonsensical (especially if we're meant to access signal constants from LibC). It's not unstable. It's unsafe and not portable but the C API is _very_ stable. Let's remember that easy interaction with C API is one of the _very_ powerful feature of Crystal...
@ysbaddaden This goes a bit off topic, but just to clarify: The Crystal namespace actually provides some features that are definitely intended for usage outside stdlib. Most inside that namespace is not intended as public API and thus nodoc, though. Especially everything in Crystal::System
.
Putting LibC is also nonsensical (especially if we're meant to access signal constants from LibC). It's not unstable.
LibC
certainly is unstable - it's not identical between platforms and some future platforms may not even have it. Symbols from LibC
may be removed between releases. I don't want to make any guarantees about stability on LibC
at all, and as such I'd like it to be moved to Crystal
. Then we can simply say "anything under Crystal
that's not documented is subject to change"
That suggestion could be nice, it'd expose usages of LibC
which are outside Crystal::System
, as only those usages would need to be prefixed Crystal::LibC
.
Still, this wouldn't get us closer to the solution of the problem at hand.
Most helpful comment
@asterite I disagree. In my opinion we should support any signal that can be elegantly replaced with a WinApi call.
Currently we have:
SendMessage(WM_CLOSE)
for GUI processes,TerminateProcess
for console processesDebugActiveProcess
DebugActiveProcessStop
TerminateProcess
The functions above are tested and (almost) work like real signals on UNIX.
Why should we only support SIGKILL/
TerminateProcess
if we have good replacements for other signals too? I would be fine with just supporting the four signals above since these are the ones you would really want to use.