2

I'm trying to create a simple echo service, which works with the systemd init system. All is OK and it's even starting after quick-deploy, BUT it exits (or crashes) quickly, when I'm trying to figure out its state via systemctl status <service> command.

The logic is simple, here I'm providing the next sources:

Program.cs

using System;
using System.Threading.Tasks;

namespace hw
{
    class Program
    {
        private const int delay = 1000;
        private static Random random = new Random();

        static async Task Handle()
        {
            Console.WriteLine($"tick tack... {random.Next()}");
            await Task.Delay(delay);
            await Handle();
        }

        static void Main(string[] args)
        {
            Task.Factory.StartNew(async() => await Handle());
            Console.ReadLine();
        }
    }
}

hw.service (systemd config file)

[Unit]
Description=HW Echo Service

[Service]
WorkingDirectory=/var/netcore/hw
ExecStart=/usr/bin/dotnet /var/netcore/hw/hw.dll
Restart=always
RestartSec=10
SyslogIdentifier=dotnet-echo-hw

[Install]
WantedBy=multi-user.target

Help-script, init.sh (you may also use chmod +x init.sh, if want to try it on your local system):

dotnet build
dotnet publish

echo "\nPreparing binaries for the service directory:\n"
rm -rf /var/netcore/hw
mkdir /var/netcore /var/netcore/hw
cp -R bin/Debug/netcoreapp1.1/publish/* /var/netcore/hw
ls -la /var/netcore/hw

echo "\nInitializing the systemd service:"
systemctl stop hw.service
rm /etc/systemd/system/hw.service
cp hw.service /etc/systemd/system/hw.service
systemctl daemon-reload
systemctl enable hw.service
systemctl start hw.service
systemctl status hw.service

Logs:

What am I waiting for?

I want my service run as other my services (ASP.NET Core) are running (with active/green state) as systemd services. As for the ASP.NET Core projects, there are NO problems, as for the simple console - they are...

How to fix my problems?

Thanks

  • But you are running in non-interactive mode, so I suppose Console.ReadLine does not work. Try something like Console.CancelKeyPress event in combination with ManualResetEvent. – Evk May 06 '17 at 09:44
  • @Evk Thanks a lot, I have done what you have suggested me & my service is working now. I didn't know about interactive mode earlier. Also, I noticed, that the current version of the .NET Core doesn't have the `Environment.UserInteractive` property. Also, there is interesting info here: http://stackoverflow.com/questions/43758423/service-detection-for-net-core , thanks a lot again. If you want, you may write an answer to this question, I shall accept it. –  May 06 '17 at 10:55
  • Glad that it helped, but as for writing answer I just not sure exactly about the reasons, don't have much experience with .net core yet. In full .NET for example, as I remember, Console.ReadLine will still work in non-interactive mode (will block) and Console.ReadKey will throw an exception. Anyhow if you are not expecting any input (running as a service) it is good to not use methods such as Console.ReadLine anyway. – Evk May 06 '17 at 11:29

1 Answers1

1

As Evk has suggested to use ManualResetEvent, I've done the next:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace hw
{
    class Program
    {
        private const int delay = 1000;
        private static Random random = new Random();
        private static ManualResetEvent resetEvent = new ManualResetEvent(false);

        static async Task Handle()
        {
            Console.WriteLine($"tick tack... {random.Next()}");
            await Task.Delay(delay);
            await Handle();
        }

        static void Main(string[] args)
        {
            Task.Factory.StartNew(async() => await Handle());
            Console.CancelKeyPress += (sender, eventArgs) => 
            {
                // Cancel the cancellation to allow the program to shutdown cleanly.
                eventArgs.Cancel = true;
                resetEvent.Set();
            };
            resetEvent.WaitOne();
        }
    }
}

Now all is working & service doesn't stop/crash.

Palindromer
  • 854
  • 1
  • 10
  • 29
  • You can replace `Task.Factory.StartNew(async() => await Handle());` with just `Handle()` by the way. – Evk May 06 '17 at 11:34
  • @Evk I've done this only because of skipping warning in Visual Code editor from OmniSharp :) As for the logic, yes, I'm agree. Factory doesn't make sense. –  May 06 '17 at 11:43
  • You can use extension method like this `public static class TaskExtensions { public static void Forget(this Task task) { } }` to clarify your intentions and remove that warning (though of course it's usually not a good idea to ignore task, because it might throw exceptions you won't observe). – Evk May 06 '17 at 12:13
  • @Evk Thanks a lot :) –  May 06 '17 at 13:04