// 
// Boost.Process 
// ~~~~~~~~~~~~~ 
// 
// Copyright (c) 2006, 2007 Julio M. Merino Vidal 
// Copyright (c) 2008, 2009 Boris Schaeling 
// 
// Distributed under the Boost Software License, Version 1.0. (See accompanying 
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 
// 

/** 
 * \file boost/process/detail/pipe.hpp 
 * 
 * Includes the declaration of the pipe class. This file is for 
 * internal usage only and must not be included by the library user. 
 */ 

#ifndef BOOST_PROCESS_DETAIL_PIPE_HPP 
#define BOOST_PROCESS_DETAIL_PIPE_HPP 

#include <boost/process/config.hpp> 

#if defined(BOOST_POSIX_API) 
#  include <unistd.h> 
#  include <cerrno> 
#elif defined(BOOST_WINDOWS_API) 
#  if defined(BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE) 
#    include <boost/lexical_cast.hpp> 
#    include <string> 
#  endif 
#  include <windows.h> 
#else 
#  error "Unsupported platform." 
#endif 

#include <boost/process/detail/file_handle.hpp> 
#include <boost/system/system_error.hpp> 
#include <boost/throw_exception.hpp> 

namespace boost { 
namespace process { 
namespace detail { 

/** 
 * Simple RAII model for anonymous pipes. 
 * 
 * The pipe class is a simple RAII model for anonymous pipes. It 
 * provides a portable constructor that allocates a new %pipe and creates 
 * a pipe object that owns the two file handles associated to it: the 
 * read end and the write end. 
 * 
 * These handles can be retrieved for modification according to 
 * file_handle semantics. Optionally, their ownership can be transferred 
 * to external \a file_handle objects which comes handy when the two 
 * ends need to be used in different places (i.e. after a POSIX fork() 
 * system call). 
 * 
 * Pipes can be copied following the same semantics as file handles. 
 * In other words, copying a %pipe object invalidates the source one. 
 * 
 * \see file_handle 
 */ 
class pipe 
{ 
public: 
    /** 
     * Creates a new %pipe. 
     * 
     * The default pipe constructor allocates a new anonymous %pipe 
     * and assigns its ownership to the created pipe object. On Windows 
     * when the macro BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE is defined 
     * a named pipe is created. This is required if asynchronous I/O 
     * should be used as asynchronous I/O is only supported by named 
     * pipes on Windows. 
     * 
     * \throw boost::system::system_error If the anonymous %pipe 
     *        creation fails. 
     */ 
    pipe() 
    { 
        file_handle::handle_type hs[2]; 

#if defined(BOOST_POSIX_API) 
        if (::pipe(hs) == -1) 
            boost::throw_exception(boost::system::system_error(boost::system::error_code(errno, boost::system::get_system_category()), "boost::process::detail::pipe::pipe: pipe(2) failed")); 
#elif defined(BOOST_WINDOWS_API) 
        SECURITY_ATTRIBUTES sa; 
        ZeroMemory(&sa, sizeof(sa)); 
        sa.nLength = sizeof(sa); 
        sa.lpSecurityDescriptor = NULL; 
        sa.bInheritHandle = FALSE; 

#  if defined(BOOST_PROCESS_WINDOWS_USE_NAMED_PIPE) 
        static unsigned int nextid = 0; 
        std::string pipe = "\\\\.\\pipe\\boost_process_" + boost::lexical_cast<std::string>(::GetCurrentProcessId()) + "_" + boost::lexical_cast<std::string>(nextid++); 
        hs[0] = ::CreateNamedPipeA(pipe.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 0, 1, 8192, 8192, 0, &sa); 
        if (hs[0] == INVALID_HANDLE_VALUE) 
            boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreateNamedPipe failed")); 
        hs[1] = ::CreateFileA(pipe.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); 
        if (hs[1] == INVALID_HANDLE_VALUE) 
            boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreateFile failed")); 

        OVERLAPPED overlapped; 
        ZeroMemory(&overlapped, sizeof(overlapped)); 
        overlapped.hEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL); 
        if (!overlapped.hEvent) 
            boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreateEvent failed")); 
        BOOL b = ::ConnectNamedPipe(hs[0], &overlapped); 
        if (!b) 
        { 
            if (::GetLastError() == ERROR_IO_PENDING) 
            { 
                if (::WaitForSingleObject(overlapped.hEvent, INFINITE) == WAIT_FAILED) 
                { 
                    ::CloseHandle(overlapped.hEvent); 
                    boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: WaitForSingleObject failed")); 
                } 
            } 
            else if (::GetLastError() != ERROR_PIPE_CONNECTED) 
            { 
                ::CloseHandle(overlapped.hEvent); 
                boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: ConnectNamedPipe failed")); 
            } 
        } 
        ::CloseHandle(overlapped.hEvent); 
#  else 
        if (!::CreatePipe(&hs[0], &hs[1], &sa, 0)) 
            boost::throw_exception(boost::system::system_error(::GetLastError(), boost::system::system_category(), "boost::process::detail::pipe::pipe: CreatePipe failed")); 
#  endif 
#endif 

        read_end_ = file_handle(hs[0]); 
        write_end_ = file_handle(hs[1]); 
    } 

    /** 
     * Returns the %pipe's read end file handle. 
     * 
     * Obtains a reference to the %pipe's read end file handle. Care 
     * should be taken to not duplicate the returned object if ownership 
     * shall remain to the %pipe. 
     * 
     * Duplicating the returned object invalidates its corresponding file 
     * handle in the %pipe. 
     * 
     * \return A reference to the %pipe's read end file handle. 
     */ 
    file_handle &rend() 
    { 
        return read_end_; 
    } 

    /** 
     * Returns the %pipe's write end file handle. 
     * 
     * Obtains a reference to the %pipe's write end file handle. Care 
     * should be taken to not duplicate the returned object if ownership 
     * shall remain to the %pipe. 
     * 
     * Duplicating the returned object invalidates its corresponding file 
     * handle in the %pipe. 
     * 
     * \return A reference to the %pipe's write end file handle. 
     */ 
    file_handle &wend() 
    { 
        return write_end_; 
    } 

private: 
    /** 
     * The %pipe's read end file handle. 
     */ 
    file_handle read_end_; 

    /** 
     * The %pipe's write end file handle. 
     */ 
    file_handle write_end_; 
}; 

} 
} 
} 

#endif