text: // Licensed to the Apache Software Foundation (ASF) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The ASF licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  #include "plasma/events.h"
  
  #include <utility>
  
  #include <errno.h>
  
  extern "C" {
  #include "ae/ae.h"
  }
  
  namespace plasma {
  
  // Verify that the constants defined in events.h are defined correctly.
  static_assert(kEventLoopTimerDone == AE_NOMORE, "constant defined incorrectly");
  static_assert(kEventLoopOk == AE_OK, "constant defined incorrectly");
  static_assert(kEventLoopRead == AE_READABLE, "constant defined incorrectly");
  static_assert(kEventLoopWrite == AE_WRITABLE, "constant defined incorrectly");
  
  void EventLoop::FileEventCallback(aeEventLoop* loop, int fd, void* context, int events) {
    FileCallback* callback = reinterpret_cast<FileCallback*>(context);
    (*callback)(events);
  }
  
  int EventLoop::TimerEventCallback(aeEventLoop* loop, TimerID timer_id, void* context) {
    TimerCallback* callback = reinterpret_cast<TimerCallback*>(context);
    return (*callback)(timer_id);
  }
  
  constexpr int kInitialEventLoopSize = 1024;
  
  EventLoop::EventLoop() { loop_ = aeCreateEventLoop(kInitialEventLoopSize); }
  
  bool EventLoop::AddFileEvent(int fd, int events, const FileCallback& callback) {
    if (file_callbacks_.find(fd) != file_callbacks_.end()) {
      return false;
    }
    auto data = std::unique_ptr<FileCallback>(new FileCallback(callback));
    void* context = reinterpret_cast<void*>(data.get());
    // Try to add the file descriptor.
    int err = aeCreateFileEvent(loop_, fd, events, EventLoop::FileEventCallback, context);
    // If it cannot be added, increase the size of the event loop.
    if (err == AE_ERR && errno == ERANGE) {
      err = aeResizeSetSize(loop_, 3 * aeGetSetSize(loop_) / 2);
      if (err != AE_OK) {
        return false;
      }
      err = aeCreateFileEvent(loop_, fd, events, EventLoop::FileEventCallback, context);
    }
    // In any case, test if there were errors.
    if (err == AE_OK) {
      file_callbacks_.emplace(fd, std::move(data));
      return true;
    }
    return false;
  }
  
  void EventLoop::RemoveFileEvent(int fd) {
    aeDeleteFileEvent(loop_, fd, AE_READABLE | AE_WRITABLE);
    file_callbacks_.erase(fd);
  }
  
  void EventLoop::Start() { aeMain(loop_); }
  
  void EventLoop::Stop() { aeStop(loop_); }
  
  void EventLoop::Shutdown() {
    if (loop_ != nullptr) {
      aeDeleteEventLoop(loop_);
      loop_ = nullptr;
    }
  }
  
  EventLoop::~EventLoop() { Shutdown(); }
  
  int64_t EventLoop::AddTimer(int64_t timeout, const TimerCallback& callback) {
    auto data = std::unique_ptr<TimerCallback>(new TimerCallback(callback));
    void* context = reinterpret_cast<void*>(data.get());
    int64_t timer_id =
        aeCreateTimeEvent(loop_, timeout, EventLoop::TimerEventCallback, context, NULL);
    timer_callbacks_.emplace(timer_id, std::move(data));
    return timer_id;
  }
  
  int EventLoop::RemoveTimer(int64_t timer_id) {
    int err = aeDeleteTimeEvent(loop_, timer_id);
    timer_callbacks_.erase(timer_id);
    return err;
  }
  
  }  // namespace plasma
  