From 70a2c42a5eef1dab2b7ab75e3c7f6ffc8c8c1959 Mon Sep 17 00:00:00 2001 From: Patrick Spek Date: Fri, 5 Feb 2021 09:55:25 +0100 Subject: Move source files into src --- ...-09-28-perl6-creating-a-background-service.adoc | 159 +++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 src/_posts/2017-09-28-perl6-creating-a-background-service.adoc (limited to 'src/_posts/2017-09-28-perl6-creating-a-background-service.adoc') diff --git a/src/_posts/2017-09-28-perl6-creating-a-background-service.adoc b/src/_posts/2017-09-28-perl6-creating-a-background-service.adoc new file mode 100644 index 0000000..4cc759d --- /dev/null +++ b/src/_posts/2017-09-28-perl6-creating-a-background-service.adoc @@ -0,0 +1,159 @@ +--- +date: 2017-09-28 15:11:43 +tags: Tutorial Perl6 Programming Raku +description: > + I've recently made some progress on Shinrin, a centralized logging system in + Perl 6. This has to run as service, which means that for most service + managers it has to be able to run in the background. +--- += Perl 6 - Creating a background service +:toc: preamble + +I've recently made some progress on +https://github.com/scriptkitties/perl6-Shinrin[Shinrin], a centralized logging +system in Perl 6. This has to run as service, which means that for most service +managers it has to be able to run in the background. + +[NOTE] +==== +If you just want to get to the solution and don't care for the details, just +head straight to link:#the-final-solution[the full script]. +==== + +== It's not possible! +After a lot of trying and talking with the folks at +irc://chat.freenode.net:6697/#perl6[#perl6] I was told that it is not possible +to do this in pure Perl 6, explained by people with more knowledge than I have +on the internals: + +[quote, jnthn] +____ +(jnthn suspects fork + multi-threaded VM = pain) Since fork only clones one +thread - the one that called it. So suddenly you've got an instance of the VM +missing most of its threads. +____ + +[quote, geekosaur] +____ +The most common failure mode is that some thread is holding e.g. a mutex (or a +userspace lock) during the fork. The thread goes away but the lock is process +level and remains, with nothing around to know to unlock it. So then things +work until something else needs that lock and suddenly you deadlock. +____ + +Not much later, `jnthn` https://github.com/perl6/doc/commit/8f9443c3ac[pushed a +commit] to update the docs to clarify that a `fork` call through `NativeCall` +will probably not give the result you were hoping for. + +== Or is it? +Luckily, the same people were able to think up of a work-around, which can be +made in POSIX sh, so it's usable on any decent OS. The workaround is to let a +little shell script fork into the background, and let that run the Perl +application. + +=== A first example +This is fairly simple to create, as in this example to launch `shinrind` in the +background: + +[source,sh] +---- +#! /usr/bin/env sh + +main() +{ + perl6 -Ilib bin/shinrind "$@" +} + +main "$@" & +---- + +This works just fine if the working directory is correct. This means you need +to be in the parent directory to `lib` and `bin` of the program to make it +work. + +== Improving the forking script +While that short script works fine to show a proof of concept, in order to make +it viable for real-world scenarios, it can use some improvements. After all, it +would be annoying if you'd have to `cd` to a specific directory any time you +want to start your application. + +=== Ensure you are in the directory you should be in +So for starters, let's make sure that you can run it from anywhere on your +system. For this, you should set the working directory for the script, so you +don't have to do it manually. Because the script runs in its own subshell, the +shell you're working from remains unaffected. + +A POSIX compliant way to get the directory the script is stored in is as +follows: + +[source,sh] +---- +DIR=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd) +---- + +This will set `$DIR` to the path of the directory the shell script is stored +in. You can simply `cd` to that and be assured you're in the right directory. + +In Perl 6, it is expected for executable files to live in the `bin` directory +of your project repository. So you should actually be in the parent of the +directory holding your script. Furthermore, you should check the `cd` command +executed correctly, just to be safe. + +[source,sh] +---- +cd -- "${DIR}/.." || exit +---- + +=== Disable `STDOUT` and `STDERR` +A started service should not be polluting your interactive shell, so you should +disable (or otherwise redirect) `STDOUT` and `STDERR`. This is done in the +shell using a small bit of code behind whatever you want to redirect: + +[source,sh] +---- +> /dev/null 2>&1 +---- + +This will set `STDOUT` to `/dev/null`, and set `STDERR` to the same stream as +`STDOUT`, which in effect will make all output go to `/dev/null`. If you want +to log everything to a single file, you can replace `/dev/null` with another +file of your choice. If you don't want logs to be overwritten on each start, +use a `>>` instead of a single `>` at the start. + +If you want to log errors and output in different files, you can use the +following: + +[source,sh] +---- +> /var/log/service.log 2> /var/log/service.err +---- + +This will put standard output in `/var/log/service.log` and errors in +`/var/log/service.err`. + +=== Fork just the Perl 6 program +In the initial example, I put the `&` behind the `main` call, at the bottom of +the script. While this works just fine for most simple usage, if you want to do +additional chores, like creating a pidfile after starting the Perl 6 program, +you're out of luck. If you were to only fork the Perl 6 application, you could +handle some other cases in the shell script. + +== The final solution +For those eager to just get going with this, here is the complete example +script to just fork your Perl program into the background: + +[source,sh] +---- +#! /usr/bin/env sh + +readonly DIR=$(CDPATH="" cd -- "$(dirname -- "$0")" && pwd) + +main() +{ + cd -- "${DIR}/.." || exit + + perl6 -Ilib bin/shinrind "$@" > /dev/null >2&1 & +} + +main "$@" +---- -- cgit v1.1