dnl -------------------------------------------------------------------------
dnl       $Id: aio.m4 85317 2009-05-11 15:21:04Z schmidt $
dnl
dnl       aio.m4
dnl
dnl       ACE M4 include file which contains ACE specific M4 macros
dnl       that determine availablility of POSIX asynchronous IO
dnl       support.
dnl
dnl -------------------------------------------------------------------------

dnl  Copyright (C) 1998, 1999, 2002  Ossama Othman
dnl
dnl  All Rights Reserved
dnl
dnl This library is free software; you can redistribute it and/or
dnl modify it under the current ACE distribution terms.
dnl
dnl This library is distributed in the hope that it will be useful,
dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

dnl Asynchronous IO check
dnl Use this macro to determine if asynchronous IO is working on a
dnl given platform.
dnl Usage: ACE_CHECK_ASYNCH_IO
AC_DEFUN([ACE_CHECK_ASYNCH_IO],
[
 AC_REQUIRE([AC_PROG_CXX])
 AC_REQUIRE([AC_PROG_CXXCPP])
 AC_LANG([C++])
 AC_REQUIRE([AC_LANG])
 AC_REQUIRE([ACE_CHECK_THREADS])

 dnl In case a library with the asynchronous libraries is found but
 dnl the asynchronous IO support is not functional then save a copy
 dnl of the list of libraries before the asynch IO function library
 dnl is added to the list so that we can revert the list to its
 dnl pre-asynch-IO check state.
 ace_save_LIBS="$LIBS"

 dnl Asynchronous IO library check
 dnl Some platforms, such as Solaris puts aio_read in -lposix4, for example.
 dnl In some cases, the thread library must be linked to in addition to the
 dnl real-time support library.  As such, make sure these checks are done
 dnl after the thread library checks.
 AC_SEARCH_LIBS([aio_read], [aio rt posix4],
    [ace_has_aio_funcs=yes], [ace_has_aio_funcs=no])

if test "$ace_has_aio_funcs" = yes; then
  ACE_CACHE_CHECK([for working asynchronous IO],
    [ace_cv_feature_aio_calls],
    [
     AC_RUN_IFELSE([AC_LANG_SOURCE([[
#ifndef ACE_LACKS_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#ifndef ACE_LACKS_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <iostream>

#include <aio.h>

class Test_Aio
{
public:
  Test_Aio (void);
  // Default constructor.

  int init (void);
  // Initting the output file and the buffer.

  int do_aio (void);
  // Doing the testing stuff.

  ~Test_Aio (void);
  // Destructor.
private:
  int out_fd_;
  // Output file descriptor.

  struct aiocb *aiocb_write_;
  // For writing to the file.

  struct aiocb *aiocb_read_;
  // Reading stuff from the file.

  char *buffer_write_;
  // The buffer to be written to the out_fd.

  char *buffer_read_;
  // The buffer to be read back from the file.
};

Test_Aio::Test_Aio (void)
  : out_fd_ (0),
    aiocb_write_ (new struct aiocb),
    aiocb_read_ (new struct aiocb),
    buffer_write_ (0),
    buffer_read_ (0)
{
}

Test_Aio::~Test_Aio (void)
{
  if (close (this->out_fd_) != 0)
    perror ("close");

  delete aiocb_write_;
  delete aiocb_read_;
  delete [] buffer_write_;
  delete [] buffer_read_;
}

// Init the output file and init the buffer.
int
Test_Aio::init (void)
{
  // Open the output file.
  this->out_fd_ = open ("test_aio.log", O_RDWR | O_CREAT | O_TRUNC, 0600);
  if (this->out_fd_ == -1)
    {
      perror ("open");
      return -1;
    }

  unlink ("test_aio.log"); // Unlink now so we don't have to do so later.

  const char message[] = "Welcome to the world of AIO... AIO Rules !!!";

  // Init the buffers.
  this->buffer_write_ = new char [sizeof (message) + 1];
  strcpy (this->buffer_write_, message);
  this->buffer_read_ = new char [sizeof (message) + 1];

  return 0;
}

// Set the necessary things for the AIO stuff.
// Write the buffer asynchly.hmm Disable signals.
// Go on aio_suspend. Wait for completion.
// Print out the result.
int
Test_Aio::do_aio (void)
{
  // = Write to the file.

  // Setup AIOCB.
  this->aiocb_write_->aio_fildes = this->out_fd_;
  this->aiocb_write_->aio_offset = 0;
  this->aiocb_write_->aio_buf = this->buffer_write_;
  this->aiocb_write_->aio_nbytes = strlen (this->buffer_write_);
  this->aiocb_write_->aio_reqprio = 0;
  this->aiocb_write_->aio_sigevent.sigev_notify = SIGEV_NONE;
  //this->this->aiocb_.aio_sigevent.sigev_signo = SIGRTMAX;
  this->aiocb_write_->aio_sigevent.sigev_value.sival_ptr =
    (void *) this->aiocb_write_;

  // Fire off the aio write.
  if (aio_write (this->aiocb_write_) != 0)
    {
      perror ("aio_write");
      return -1;
    }

  // = Read from that file.

  // Setup AIOCB.
  this->aiocb_read_->aio_fildes = this->out_fd_;
  this->aiocb_read_->aio_offset = 0;
  this->aiocb_read_->aio_buf = this->buffer_read_;
  this->aiocb_read_->aio_nbytes = strlen (this->buffer_write_);
  this->aiocb_read_->aio_reqprio = 0;
  this->aiocb_read_->aio_sigevent.sigev_notify = SIGEV_NONE;
  //this->this->aiocb_.aio_sigevent.sigev_signo = SIGRTMAX;
  this->aiocb_read_->aio_sigevent.sigev_value.sival_ptr =
    (void *) this->aiocb_read_;

  // Fire off the aio write. If it doesnt get queued, carry on to get
  // the completion for the first one.
  if (aio_read (this->aiocb_read_) < 0)
    perror ("aio_read");

  // Wait for the completion on aio_suspend.
  struct aiocb *list_aiocb[2];
  list_aiocb [0] = this->aiocb_write_;
  list_aiocb [1] = this->aiocb_read_;

  // Do suspend till all the aiocbs in the list are done.
  int done = 0;
  while (!done)
    {
      if (aio_suspend (list_aiocb, 2, 0) != 0)
        {
          perror ("aio_suspend");
          return -1;
        }

      // Analyze return and error values.
      if (list_aiocb [0] != 0 && aio_error (list_aiocb [0]) != EINPROGRESS)
        {
          if (aio_return (list_aiocb [0]) == -1)
            {
              perror ("aio_return");
              return -1;
            }
          else
            {
              // Successful. Store the pointer somewhere and make the
              // entry NULL in the list.
              // @@ no need ----> this->aiocb_write_ = list_aiocb [0];
              list_aiocb [0] = 0;
            }
        }

      if (list_aiocb [1] != 0 && aio_error (list_aiocb [1]) != EINPROGRESS)
        {
          if (aio_return (list_aiocb [1]) == -1)
            {
              perror ("aio_return");
              return -1;
            }
          else
            {
              // Successful. Store the pointer somewhere and make the
              // entry NULL in the list.
              // @@ no need ----> this->aiocb_read_ = list_aiocb [1];
              list_aiocb [1] = 0;
            }
        }

      // Is it done?
      if ((list_aiocb [0] == 0) && (list_aiocb [1] == 0))
        done = 1;
    }

  return 0;
}

int
main ()
{
  Test_Aio test_aio;

  if (test_aio.init () != 0)
    {
      //printf ("AIOCB test failed:\n"
      //        "ACE_POSIX_AIOCB_PROACTOR may not work in this platform\n");
      return -1;
    }

  if (test_aio.do_aio () != 0)
    {
      //printf ("AIOCB test failed:\n"
      //        "ACE_POSIX_AIOCB_PROACTOR may not work in this platform\n");
      return -1;
    }
  //printf ("AIOCB test successful:\n"
  //        "ACE_POSIX_AIOCB_PROACTOR should work in this platform\n");
  return 0;
}
       ]])],[
        ace_cv_feature_aio_calls=yes
       ],[
        ace_cv_feature_aio_calls=no
       ],[
        dnl Asynchronous IO test for cross-compiled platforms
        dnl This test is weaker than the above run-time tests but it will
        dnl have to do.
        AC_COMPILE_IFELSE(
          [AC_LANG_PROGRAM([[
#include <aio.h>
          ]],
          [[
           aiocb* aiocb_ptr (void);
          ]])],
          [
           ace_cv_feature_aio_calls=yes
          ],
          [
           ace_cv_feature_aio_calls=no
          ])
       ])
    ],[AC_DEFINE([ACE_HAS_AIO_CALLS])],[LIBS="$ace_save_LIBS"])
fi dnl test "$ace_has_aio_funcs" = yes


if test "$ace_cv_feature_aio_calls" = yes; then
  ACE_CACHE_CHECK([for working POSIX realtime signals],
    [ace_cv_feature_posix_rt_sigs],
    [
      dnl Create a file for the test program to read.
      cat > test_aiosig.txt <<EOF

*******************************************************
FOO BAR FOO BAR FOO BAR FOO BAR FOO BAR FOO BAR FOO BAR
*******************************************************
EOF


      AC_RUN_IFELSE(
        [AC_LANG_SOURCE([[
extern "C" {
#include <signal.h>
}
#ifndef ACE_LACKS_UNISTD_H
#include <unistd.h>
#endif
#include <fcntl.h>
#ifndef ACE_LACKS_SYS_TYPES_H
# include <sys/types.h>
#endif
#include <sys/stat.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#include <limits.h>

#include <aio.h>

#ifdef __cplusplus
extern "C"
#endif
void null_handler (int /* signal_number */,
                   siginfo_t * /* info */,
                   void * /* context */);

int file_handle = -1;
char mb1[BUFSIZ + 1];
char mb2[BUFSIZ + 1];
aiocb aiocb1, aiocb2;
sigset_t completion_signal;

// Function prototypes.
int setup_signal_delivery (void);
int issue_aio_calls (void);
int query_aio_completions (void);
int test_aio_calls (void);
int setup_signal_handler (void);
int setup_signal_handler (int signal_number);

int
setup_signal_delivery (void)
{
  // Make the sigset_t consisting of the completion signal.
  if (sigemptyset (&completion_signal) == -1)
    {
      perror ("Error:Couldn't init the RT completion signal set\n");
      return -1;
    }

  if (sigaddset (&completion_signal, SIGRTMIN) == -1)
    {
      perror ("Error:Couldn't init the RT completion signal set\n");
      return -1;
    }

  // Mask them.
  if (pthread_sigmask (SIG_BLOCK, &completion_signal, 0) == -1)
    {
      perror ("Error:Couldn't make the RT completion signals\n");
      return -1;
    }

  return setup_signal_handler (SIGRTMIN);
}

int
issue_aio_calls (void)
{
  // Setup AIOCB.
  aiocb1.aio_fildes = file_handle;
  aiocb1.aio_offset = 0;
  aiocb1.aio_buf = mb1;
  aiocb1.aio_nbytes = BUFSIZ;
  aiocb1.aio_reqprio = 0;
  aiocb1.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  aiocb1.aio_sigevent.sigev_signo = SIGRTMIN;
  aiocb1.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb1;

  // Fire off the aio write.
  if (aio_read (&aiocb1) == -1)
    {
      // Queueing failed.
      perror ("Error:Asynch_Read_Stream: aio_read queueing failed\n");
      return -1;
    }

  // Setup AIOCB.
  aiocb2.aio_fildes = file_handle;
  aiocb2.aio_offset = BUFSIZ + 1;
  aiocb2.aio_buf = mb2;
  aiocb2.aio_nbytes = BUFSIZ;
  aiocb2.aio_reqprio = 0;
  aiocb2.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
  aiocb2.aio_sigevent.sigev_signo = SIGRTMIN;
  aiocb2.aio_sigevent.sigev_value.sival_ptr = (void *) &aiocb2;

  // Fire off the aio write.
  if (aio_read (&aiocb2) == -1)
    {
      // Queueing failed.
      perror ("Error:Asynch_Read_Stream: aio_read queueing failed\n");
      return -1;
    }
  return 0;
}

int
query_aio_completions (void)
{
  int result = 0;
  size_t number_of_completions = 0;
  for (number_of_completions = 0;
       number_of_completions < 2;
       number_of_completions++)
    {
      // Wait for <milli_seconds> amount of time.
      // @@ Assigning <milli_seconds> to tv_sec.
      timespec timeout;
      timeout.tv_sec = 5;
      timeout.tv_nsec = 0;

      // To get back the signal info.
      siginfo_t sig_info;

      // Await the RT completion signal.
      int sig_return = sigtimedwait (&completion_signal,
                                     &sig_info,
                                     &timeout);

      // Error case.
      // If failure is coz of timeout, then return *0* but set
      // errno appropriately. This is what the WinNT proactor
      // does.
      if (sig_return == -1)
        {
          perror ("Error:Error waiting for RT completion signals\n");
          return -1;
        }

      // RT completion signals returned.
      if (sig_return != SIGRTMIN)
        {
          //printf ("Unexpected signal (%d) has been received while waiting for RT Completion Signals\n",
          //        sig_return);
          return -1;
        }

      // @@ Debugging.
      //printf ("Sig number found in the sig_info block : %d\n",
      //        sig_info.si_signo);

      // Is the signo returned consistent?
      if (sig_info.si_signo != sig_return)
        {
          //printf ("Inconsistent signal number (%d) in the signal info block\n",
          //        sig_info.si_signo);
          return -1;
        }

      // @@ Debugging.
      //printf ("Signal code for this signal delivery : %d\n",
      //        sig_info.si_code);

      // Is the signal code an aio completion one?
      if ((sig_info.si_code != SI_ASYNCIO) &&
          (sig_info.si_code != SI_QUEUE))
        {
          //printf ("Unexpected signal code (%d) returned on completion querying\n",
          //        sig_info.si_code);
          return -1;
        }

      // Retrive the aiocb.
      aiocb* aiocb_ptr = (aiocb *) sig_info.si_value.sival_ptr;

      // Analyze error and return values. Return values are
      // actually <errno>'s associated with the <aio_> call
      // corresponding to aiocb_ptr.
      int error_code = aio_error (aiocb_ptr);
      if (error_code == -1)
        {
          perror ("Error:Invalid control block was sent to <aio_error> for compleion querying\n");
          return -1;
        }

      if (error_code != 0)
        {
          // Error occurred in the <aio_>call. Return the errno
          // corresponding to that <aio_> call.
          //printf ("Error:An AIO call has failed:Error code = %d\n",
          //        error_code);
          return -1;
        }

      // No error occured in the AIO operation.
      int nbytes = aio_return (aiocb_ptr);
      if (nbytes == -1)
        {
          perror ("Error:Invalid control block was sent to <aio_return>\n");
          return -1;
        }

      //if (number_of_completions == 0)
        // Print the buffer.
        //printf ("Number of bytes transferred : %d\n The buffer : %s \n",
        //        nbytes,
        //        mb1);
      //else
        // Print the buffer.
        //printf ("Number of bytes transferred : %d\n The buffer : %s \n",
        //        nbytes,
        //        mb2);
    }
  return 0;
}

int
test_aio_calls (void)
{
  // Set up the input file.
  // Open file (in SEQUENTIAL_SCAN mode)
  file_handle = open ("test_aiosig.txt", O_RDONLY);

  if (file_handle == -1)
    {
      perror ("open");
      return -1;
    }

  unlink ("test_aiosig.txt"); // Unlink now so we don't have to do so later.

  if (setup_signal_delivery () < 0)
    return -1;

  if (issue_aio_calls () < 0)
    return -1;

  if (query_aio_completions () < 0)
    return -1;

  if (close (file_handle) != 0)
    {
      perror ("close");
      return -1;
    }

  return 0;
}

int
setup_signal_handler (int signal_number)
{
   // Setting up the handler(!) for these signals.
  struct sigaction reaction;
  sigemptyset (&reaction.sa_mask);   // Nothing else to mask.
  reaction.sa_flags = SA_SIGINFO;    // Realtime flag.
#if defined (SA_SIGACTION)
  // Lynx says, it is better to set this bit to be portable.
  reaction.sa_flags &= SA_SIGACTION;
#endif /* SA_SIGACTION */
  reaction.sa_sigaction = null_handler;     // Null handler.
  int sigaction_return = sigaction (SIGRTMIN,
                                    &reaction,
                                    0);
  if (sigaction_return == -1)
    {
      perror ("Error:Proactor couldn't do sigaction for the RT SIGNAL");
      return -1;
    }

  return 0;
}

void
null_handler (int         /* signal_number */,
              siginfo_t * /* info */,
              void *      /* context */)
{
}

int
main ()
{
  if (test_aio_calls () == 0)
    {
      // printf ("RT SIG test successful:\n"
      //         "ACE_POSIX_SIG_PROACTOR should work in this platform\n");
      return 0;
    }

  //printf ("RT SIG test failed:\n"
  //        "ACE_POSIX_SIG_PROACTOR may not work in this platform\n");
  return -1;

}
        ]])],
        [
          ace_cv_feature_posix_rt_sigs=yes
        ],
        [
          ace_cv_feature_posix_rt_sigs=no
        ],
        [
          dnl Don't bother doing anything for cross-compiling here
          dnl since the basic aio run-time test will prevent this
          dnl rt sig run-time test from ever running when cross-compiling.
          dnl We just put something in here to prevent autoconf
          dnl from complaining.
          ace_just_a_place_holder=ignoreme
        ])
    ],[AC_DEFINE([ACE_HAS_POSIX_REALTIME_SIGNALS])],[])
fi dnl test "$ace_cv_feature_aio_calls" = yes

])
