<commit_before># beckon

A Clojure library to handle POSIX signals in JVM applications with style and
grace. Sets up with the dirty parts and let you work with it in a (relatively)
simple fashion.

## Quick-start

Add the following dependency to your `project.clj` file:

```clj
[beckon "0.1.0-SNAPSHOT"]
```

Say you want to grab `SIGINT` and, say, ignore every interruption. That is done
as follows:

```clj
(require 'beckon)

(reset! (beckon/signal-atom "INT") [(fn [])])
```

That's it—it's not harder than that. Of course, you may not believe me, so it's
good that we can confirm whether it's possible by using the `raise!` function:

```clj
(beckon/raise! "INT")
; Nothing happens...
(beckon/raise! "TERM")
; NB: This will terminate your JVM process.
```

If you want to go back to the original setup, use `reinit!`:

```clj
(beckon/reinit! "INT")
; Reinitializes the SIGINT signal handler.
(beckon/raise! "INT")
; NB: This will terminate your JVM process.
```

And well, that's really all you need in order to work with beckon.

## Usage

The core of beckon consist of 4 functions: `signal-atom`, `raise!`, `reinit!`
and `reinit-all!`. In addition, there are 2 utility-functions, `always-true` and
`always-false`.

### `signal-atom`

`signal-atom` is the core piece of this library and (ab)uses atoms to setup
signal handlers for Clojurians. As you'd guess, this returns an atom. The atom
has a validator function attached to it, so only Seqable collections where every
element are Callable are legal values in this atom. All Clojure functions
implements Callable, but only functions which has a zero-argument invokation
will actually work as a callable.

We require the contents of the atom to be a Seqable of Callables because it
makes it possible to add multiple signal handlers to a single signal. The signal
handlers will be conditionally executed based upon whether the previous signal
handler returned a truthy of falsey value. If the handler returned a truthy
value, it will continue to execute the other handler, otherwise it will stop.
For example:

```clj
(reset! (beckon/signal-atom "INT")
        [(fn [] (println "foo") true)
         (fn [] (println "bar") false)
         (fn [] (println "We'll never see this"))])
```

Will only print `foo` and `bar`.

Is this sensible? I'm not sure. This certainly complects the library, but I've
very seldom needed more than a single signal handler. For normal applications,
this functionality shouldn't be too hard to reason around. However, if people
have complaints about this functionality, I would certainly like to hear from
you.

The signal handler is automatically updated whenever the atom is updated, but
not vice versa. So if you use beckon, please don't try and hack in signal
handling through another library or through the native java interface. Trust me,
the complexity will kill you if you do.

If an Exception is thrown by a handler, it will be treated as a falsey value.
Errors will not be caught and will crash the Thread.

### `raise!`

`raise!` is probably the most understandable

### `reinit!` and `reinit-all!`

TODO

### utility fns

TODO - clean up that mess
<commit_msg>Update readme.<commit_after># beckon

A Clojure library to handle POSIX signals in JVM applications with style and
grace. Sets up with the dirty parts and let you work with it in a (relatively)
simple fashion.

## Quick-start

Add the following dependency to your `project.clj` file:

```clj
[beckon "0.1.0-SNAPSHOT"]
```

Say you want to grab `SIGINT` and, say, print "Hahah, nothing can stop me!"
whenever someone attempts to interrupt the process. Hit up your Emacs nREPL
through `nrepl-jack-in` or similar methods, then write the following:

```clj
(require 'beckon)

(let [print-function (fn [] (println "Hahah, nothing can stop me!"))]
  (reset! (beckon/signal-atom "INT") #{print-function}))
```

That's it—it's not harder than that. Of course, you may not believe me, so it's
good that we can confirm whether it's possible by using the `raise!` function:

```clj
(beckon/raise! "INT")
; prints nothing
```

You may think I've really lied, but the thing is, this is ran on a newly spawned
thread with maximum priority. As such, it will not show in the nrepl window.
Move over to the `*nrepl-server*` buffer instead, and you'll see the message. In
case you really do not believe me, you could try out to raise the SIGTERM signal
instead:

```clj
(beckon/raise! "TERM")
; NB: This will terminate nREPL.
```

And if you didn't and want to go back to the original setup, use `reinit!`:

```clj
(beckon/reinit! "INT")
; Reinitializes the SIGINT signal handler.
(beckon/raise! "INT")
; NB: This will terminate your JVM process.
```

And well, that's really all you need in order to work with beckon.

## Usage

The core of beckon consist of 4 functions: `signal-atom`, `raise!`, `reinit!`
and `reinit-all!`. `signal-atom` is the only one needed in production systems,
usually. The other functions help out with debugging and resetting signal
handling back to "factory settings"—the initial setup of signal handlers when
the JVM starts up.

### `signal-atom`

`signal-atom` is the core piece of this library and (ab)uses atoms to setup
signal handlers for Clojurians. As you'd guess, this returns an atom. The atom
has a validator function attached to it, so only Seqable collections where every
element are Runnable are legal values in this atom. All Clojure functions
implements Runnable, but only functions which has a zero-argument invokation
will actually work as a Runnable.

Beckon require the contents of the atom to be a Seqable of Runnable because it
makes it possible to add multiple independend signal handlers to a single
signal. The signal handlers will always be executed sequentially, the only
exception is if one of the functions throw an exception or error. If a function
throws an exception, the signal handling will be cancelled (but no exception
will be thrown), and if a function throws an error, the whole signal handling
crashes. You can in theory "abuse" it to get conditional function dispatching.
For example:

```clj
(reset! (beckon/signal-atom "INT")
        [(fn [] (println "foo"))
         (fn [] (println "bar") (throw (Exception.)))
         (fn [] (println "We'll never see this"))])
```

Will only print `foo` and `bar`.

It's not really a good way to do dispatching though, so you're advised to do
this kind of logic within functions whenever possible.

The signal handler is automatically updated whenever the atom is updated, but
not vice versa. So if you use beckon, please don't try and hack in signal
handling through another library or through the native java interface.

### `raise!`

`raise!` is probably the most understandable function in the system. It will
send off a signal of the type given as input. For instance, `(beckon/raise!
"INT")` will act as if a SIGINT signal was sent to the JVM process. It's handy
to check out that your signal handlers work as intended.

### `reinit!` and `reinit-all!`

TODO

{'fraction_non_alphanumeric': 0.08511548331907613, 'fraction_numerical': 0.0012831479897348161, 'mean_word_length': 4.274436090225564, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3152139', 'n_tokens_mistral': 2087, 'n_tokens_neox': 1988, 'n_words': 1070}