summaryrefslogtreecommitdiffstats
path: root/mojo/public/cpp/system/watcher.cc
blob: 57235338fe98c5f58ef05d4c44957dfc95add000 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "mojo/public/cpp/system/watcher.h"

#include "base/bind.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/thread_task_runner_handle.h"
#include "mojo/public/c/system/functions.h"

namespace mojo {

class Watcher::MessageLoopObserver
    : public base::MessageLoop::DestructionObserver {
 public:
  explicit MessageLoopObserver(Watcher* watcher) : watcher_(watcher) {
    base::MessageLoop::current()->AddDestructionObserver(this);
  }

  ~MessageLoopObserver() override {
    StopObservingIfNecessary();
  }

 private:
  // base::MessageLoop::DestructionObserver:
  void WillDestroyCurrentMessageLoop() override {
    StopObservingIfNecessary();
    if (watcher_->IsWatching())
      watcher_->OnHandleReady(MOJO_RESULT_ABORTED);
  }

  void StopObservingIfNecessary() {
    if (is_observing_) {
      is_observing_ = false;
      base::MessageLoop::current()->RemoveDestructionObserver(this);
    }
  }

  bool is_observing_ = true;
  Watcher* watcher_;

  DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
};

Watcher::Watcher()
    : task_runner_(base::ThreadTaskRunnerHandle::Get()),
      weak_factory_(this) {
  weak_self_ = weak_factory_.GetWeakPtr();
}

Watcher::~Watcher() {
  if(IsWatching())
    Cancel();
}

bool Watcher::IsWatching() const {
  DCHECK(thread_checker_.CalledOnValidThread());
  return handle_.is_valid();
}

MojoResult Watcher::Start(Handle handle,
                          MojoHandleSignals signals,
                          const ReadyCallback& callback) {
  DCHECK(thread_checker_.CalledOnValidThread());
  DCHECK(!IsWatching());
  DCHECK(!callback.is_null());

  message_loop_observer_.reset(new MessageLoopObserver(this));
  callback_ = callback;
  handle_ = handle;
  MojoResult result = MojoWatch(handle_.value(), signals,
                                &Watcher::CallOnHandleReady,
                                reinterpret_cast<uintptr_t>(this));
  if (result != MOJO_RESULT_OK) {
    handle_.set_value(kInvalidHandleValue);
    callback_.Reset();
    message_loop_observer_.reset();
    DCHECK(result == MOJO_RESULT_FAILED_PRECONDITION ||
           result == MOJO_RESULT_INVALID_ARGUMENT);
    return result;
  }

  return MOJO_RESULT_OK;
}

void Watcher::Cancel() {
  DCHECK(thread_checker_.CalledOnValidThread());

  // The watch may have already been cancelled if the handle was closed.
  if (!handle_.is_valid())
    return;

  MojoResult result =
      MojoCancelWatch(handle_.value(), reinterpret_cast<uintptr_t>(this));
  message_loop_observer_.reset();
  // |result| may be MOJO_RESULT_INVALID_ARGUMENT if |handle_| has closed, but
  // OnHandleReady has not yet been called.
  DCHECK(result == MOJO_RESULT_INVALID_ARGUMENT || result == MOJO_RESULT_OK);
  handle_.set_value(kInvalidHandleValue);
  callback_.Reset();
}

void Watcher::OnHandleReady(MojoResult result) {
  DCHECK(thread_checker_.CalledOnValidThread());

  ReadyCallback callback = callback_;
  if (result == MOJO_RESULT_CANCELLED) {
    message_loop_observer_.reset();
    handle_.set_value(kInvalidHandleValue);
    callback_.Reset();
  }

  // NOTE: It's legal for |callback| to delete |this|.
  if (!callback.is_null())
    callback.Run(result);
}

// static
void Watcher::CallOnHandleReady(uintptr_t context,
                                MojoResult result,
                                MojoHandleSignalsState signals_state) {
  // NOTE: It is safe to assume the Watcher still exists because this callback
  // will never be run after the Watcher's destructor.
  //
  // TODO: Maybe we should also expose |signals_state| throught he Watcher API.
  // Current HandleWatcher users have no need for it, so it's omitted here.
  Watcher* watcher = reinterpret_cast<Watcher*>(context);
  watcher->task_runner_->PostTask(
      FROM_HERE,
      base::Bind(&Watcher::OnHandleReady, watcher->weak_self_, result));
}

}  // namespace mojo