// Copyright 2010-2021, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef MOZC_IPC_NAMED_EVENT_H_
#define MOZC_IPC_NAMED_EVENT_H_

#include <cstddef>
#include <string>

#include "absl/time/time.h"

#ifdef _WIN32
#include <windows.h>
#else  // _WIN32
#include <semaphore.h>
#endif  // _WIN32

// Named Event class for Inter Process Communication:
// This is a shallow wrapper for Windows CreateEvent() API.
// For Linux/Mac, emulate the CreateEvent behavior
// with SysV Semaphore
//
// Example:
//
// (Process 1)
// NamedEventListener listener("foo");  // Create named event listener named foo
// CHECK(listener.IsAvailable());
// listener.Wait(absl::Seconds(10));  // Wait until an event comes.
// Access shared resource
//
// (Process 2)
// NamedEventNotifier notifier("foo");  // Open named event
// CHECK(notifier.IsAvailable())
// send signals to the listener blocked with Wait() method
// notifier.Notify();
//
// After mozc UI launches mozc_server.exe,
// mozc UI needs to wait until mozc_server is ready.
// For this purpose, mozc UI makes a listener object.
//  When mozc_server completes all initialization and
// is ready for conversion, mozc_server sends a notification
// event to the client.
//
// Example
// - UI process
// if (mozc_server is not running) {
//   CreateProcess("mozc_server.exe" ...);
//   mozc::NamedEventListener l("session");
//   Display Splash Window
//   l.Wait(absl::Seconds(10));
//   Close splash window
//   Start conversion
// }
//
// - Server process
//  Initialize all static objects
//  Start session server
//  mozc::NamedEventNotifier n("session");
//  n.Notify();

namespace mozc {

// Util class for Named Event
class NamedEventUtil {
 public:
  NamedEventUtil(const NamedEventUtil &) = delete;
  NamedEventUtil &operator=(const NamedEventUtil &) = delete;
  // return real event name
  // Windows: <kEventPathPrefix>.<sid>.<name>
  // Linux/Mac: <Util::GetUserProfileDirectory()>/.<name>.event
  static std::string GetEventPath(const char *name);
};

class NamedEventListener {
 public:
  // Create Named Event named "name"
  // Windows: <kEventPathPrefix>.<sid>.<name>
  // Linux/Mac: <Util::GetUserProfileDirectory()>/.event.<name>
  explicit NamedEventListener(const char *name);
  virtual ~NamedEventListener();

  NamedEventListener(const NamedEventListener &) = delete;
  NamedEventListener &operator=(const NamedEventListener &) = delete;

  NamedEventListener(NamedEventListener &&) = delete;
  NamedEventListener &operator=(NamedEventListener &&) = delete;

  // Return true if NamedEventListener is available
  bool IsAvailable() const;

  // return true if NamedEventListener is created by this instance.
  // If NamedEventListener opened an exiting handle, return false.
  bool IsOwner() const;

  // Wait until the listener receives a notification
  // event from NamedEventNotifier.
  // You can set the timeout
  // if timeout is negative, Waits forever.
  bool Wait(absl::Duration msec);

  // Wait until the listener receives a notification or
  // the process specified with pid is terminated.
  // return TIMEOUT: reached timeout
  // return EVENT_SIGNALED: event is signaled.
  // return PROCESS_SIGNALED: process is signaled.
  // NOTE: Only available on Windows and Linux.
  enum {
    TIMEOUT = 0,
    EVENT_SIGNALED = 1,
    PROCESS_SIGNALED = 2,
  };
  int WaitEventOrProcess(absl::Duration msec, size_t pid);

 private:
  bool is_owner_;

#ifdef _WIN32
  HANDLE handle_;
#else   // _WIN32
  sem_t *sem_;
  std::string key_filename_;
#endif  // _WIN32
};

class NamedEventNotifier {
 public:
  // Open Named event named "name"
  // The named event object should be created by
  // NamedEventListener before calling the constructor
  explicit NamedEventNotifier(const char *name);
  virtual ~NamedEventNotifier();

  // return true if NamedEventNotifier is available
  bool IsAvailable() const;

  // send a wake-up signal to NamedEventListener
  bool Notify();

 private:
#ifdef _WIN32
  HANDLE handle_;
#else   // _WIN32
  sem_t *sem_;
#endif  // _WIN32
};
}  // namespace mozc

#endif  // MOZC_IPC_NAMED_EVENT_H_
