
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//
//


#include "runnable.hh"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <sys/errno.h>
#include <signal.h>

void
runnable::start()
{
  //
  // Note that if signals are used, all threads should block the signals
  // except the ones that are receiving them.  This makes sure SIGUSR1
  // and SIGUSR2 are blocked in all threads, in case they're used in
  // the program.
  //
  sigset_t ss;
  sigemptyset(&ss);
  sigaddset(&ss, SIGUSR1);
  sigaddset(&ss, SIGUSR2);
  int error = pthread_sigmask(SIG_BLOCK, &ss, NULL);

  if(error)
    {
      printf("Error setting signals to ignore in runnable::start()\n");
      exit(20);
    }

  error = pthread_create(&thread_id, 
			 NULL, 
			 (void* (*)(void *))runnable::static_run, 
			 (void*)this);

  if(error)
    {
      printf("Can't create a thread for a runnable class:  ");

      if(error==ENOMEM)
	printf("No memory.\n");
      else if(error==EINVAL)
	printf("Bad attributes.\n");
      else if(error==EPERM)
	printf("Insufficient Permissions\n");
      else
	printf("Unknown error number %d (0x%x)\n", error, error);

      exit(20);
    }
}


runnable::~runnable()
{
  if(thread_id)
    {
      pthread_cancel(thread_id);
      pthread_join(thread_id, 0);
    }
}


void runnable::wait(int milliseconds)
{
  struct timespec t;
  struct timeval now;
  pthread_mutex_t ptcm;
  pthread_cond_t  ptc;

  pthread_mutex_init(&ptcm, NULL);
  pthread_cond_init(&ptc, NULL);

  gettimeofday(&now, NULL);
  t.tv_sec = now.tv_sec + milliseconds/1000;
  t.tv_nsec = ( (milliseconds%1000) * 1000L + now.tv_usec ) * 1000;

  pthread_cond_timedwait(&ptc, &ptcm, &t);

  pthread_testcancel();
}
