nr_timer.cpp (7943B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* This Source Code Form is subject to the terms of the Mozilla Public 8 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 9 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 10 11 // Original code by: ekr@rtfm.com 12 13 // Implementation of the NR timer interface 14 15 // Some code here copied from nrappkit. The license was. 16 17 /** 18 Copyright (C) 2004, Network Resonance, Inc. 19 Copyright (C) 2006, Network Resonance, Inc. 20 All Rights Reserved 21 22 Redistribution and use in source and binary forms, with or without 23 modification, are permitted provided that the following conditions 24 are met: 25 26 1. Redistributions of source code must retain the above copyright 27 notice, this list of conditions and the following disclaimer. 28 2. Redistributions in binary form must reproduce the above copyright 29 notice, this list of conditions and the following disclaimer in the 30 documentation and/or other materials provided with the distribution. 31 3. Neither the name of Network Resonance, Inc. nor the name of any 32 contributors to this software may be used to endorse or promote 33 products derived from this software without specific prior written 34 permission. 35 36 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 37 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 38 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 39 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 40 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 41 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 42 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 43 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 44 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 45 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 46 POSSIBILITY OF SUCH DAMAGE. 47 48 49 ekr@rtfm.com Sun Feb 22 19:35:24 2004 50 */ 51 52 #include <string> 53 54 #include "mozilla/DebugOnly.h" 55 #include "mozilla/UniquePtr.h" 56 #include "nsCOMPtr.h" 57 #include "nsIEventTarget.h" 58 #include "nsINamed.h" 59 #include "nsITimer.h" 60 #include "nsNetCID.h" 61 #include "nsServiceManagerUtils.h" 62 #include "runnable_utils.h" 63 64 extern "C" { 65 // clang-format off 66 #include "async_wait.h" 67 #include "async_timer.h" 68 #include "r_errors.h" 69 #include "r_log.h" 70 // clang-format on 71 } 72 73 namespace mozilla { 74 75 class nrappkitCallback { 76 public: 77 nrappkitCallback(NR_async_cb cb, void* cb_arg, const char* function, int line) 78 : cb_(cb), cb_arg_(cb_arg), function_(function), line_(line) {} 79 virtual ~nrappkitCallback() = default; 80 81 virtual void Cancel() = 0; 82 83 protected: 84 /* additional members */ 85 NR_async_cb cb_; 86 void* cb_arg_; 87 std::string function_; 88 int line_; 89 }; 90 91 class nrappkitTimerCallback : public nrappkitCallback, 92 public nsITimerCallback, 93 public nsINamed { 94 public: 95 // We're going to release ourself in the callback, so we need to be threadsafe 96 NS_DECL_THREADSAFE_ISUPPORTS 97 NS_DECL_NSITIMERCALLBACK 98 99 nrappkitTimerCallback(NR_async_cb cb, void* cb_arg, const char* function, 100 int line) 101 : nrappkitCallback(cb, cb_arg, function, line), timer_(nullptr) {} 102 103 void SetTimer(already_AddRefed<nsITimer>&& timer) { timer_ = timer; } 104 105 virtual void Cancel() override { 106 AddRef(); // Cancelling the timer causes the callback it holds to 107 // be released. AddRef() keeps us alive. 108 timer_->Cancel(); 109 timer_ = nullptr; 110 Release(); // Will cause deletion of this object. 111 } 112 113 NS_IMETHOD 114 GetName(nsACString& aName) override { 115 aName.AssignLiteral("nrappkitTimerCallback"); 116 return NS_OK; 117 } 118 119 private: 120 nsCOMPtr<nsITimer> timer_; 121 virtual ~nrappkitTimerCallback() = default; 122 }; 123 124 NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback, nsINamed) 125 126 NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer* timer) { 127 r_log(LOG_GENERIC, LOG_DEBUG, "Timer callback fired (set in %s:%d)", 128 function_.c_str(), line_); 129 MOZ_RELEASE_ASSERT(timer == timer_); 130 cb_(nullptr, 0, cb_arg_); 131 132 // Allow the timer to go away. 133 timer_ = nullptr; 134 return NS_OK; 135 } 136 137 class nrappkitScheduledCallback : public nrappkitCallback { 138 public: 139 nrappkitScheduledCallback(NR_async_cb cb, void* cb_arg, const char* function, 140 int line) 141 : nrappkitCallback(cb, cb_arg, function, line) {} 142 143 void Run() { 144 if (cb_) { 145 cb_(nullptr, 0, cb_arg_); 146 } 147 } 148 149 virtual void Cancel() override { cb_ = nullptr; } 150 151 ~nrappkitScheduledCallback() = default; 152 }; 153 154 } // namespace mozilla 155 156 using namespace mozilla; 157 158 static nsCOMPtr<nsIEventTarget> GetSTSThread() { 159 nsresult rv; 160 161 nsCOMPtr<nsIEventTarget> sts_thread; 162 163 sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); 164 MOZ_ASSERT(NS_SUCCEEDED(rv)); 165 166 return sts_thread; 167 } 168 169 // These timers must only be used from the STS thread. 170 // This function is a helper that enforces that. 171 static void CheckSTSThread() { 172 DebugOnly<nsCOMPtr<nsIEventTarget>> sts_thread = GetSTSThread(); 173 174 ASSERT_ON_THREAD(sts_thread.value); 175 } 176 177 static int nr_async_timer_set_zero(NR_async_cb cb, void* arg, char* func, int l, 178 nrappkitCallback** handle) { 179 nrappkitScheduledCallback* callback( 180 new nrappkitScheduledCallback(cb, arg, func, l)); 181 182 nsresult rv = GetSTSThread()->Dispatch( 183 WrapRunnable(UniquePtr<nrappkitScheduledCallback>(callback), 184 &nrappkitScheduledCallback::Run), 185 NS_DISPATCH_NORMAL); 186 if (NS_FAILED(rv)) return R_FAILED; 187 188 *handle = callback; 189 190 // On exit to this function, the only strong reference to callback is in 191 // the Runnable. Because we are redispatching to the same thread, 192 // this is always safe. 193 return 0; 194 } 195 196 static int nr_async_timer_set_nonzero(int timeout, NR_async_cb cb, void* arg, 197 char* func, int l, 198 nrappkitCallback** handle) { 199 nsresult rv; 200 CheckSTSThread(); 201 202 nrappkitTimerCallback* callback = new nrappkitTimerCallback(cb, arg, func, l); 203 204 nsCOMPtr<nsITimer> timer; 205 rv = NS_NewTimerWithCallback(getter_AddRefs(timer), callback, timeout, 206 nsITimer::TYPE_ONE_SHOT); 207 if (NS_FAILED(rv)) { 208 return R_FAILED; 209 } 210 211 // Move the ownership of the timer to the callback object, which holds the 212 // timer alive per spec. 213 callback->SetTimer(timer.forget()); 214 215 *handle = callback; 216 217 return 0; 218 } 219 220 int NR_async_timer_set(int timeout, NR_async_cb cb, void* arg, char* func, 221 int l, void** handle) { 222 CheckSTSThread(); 223 224 nrappkitCallback* callback; 225 int r; 226 227 if (!timeout) { 228 r = nr_async_timer_set_zero(cb, arg, func, l, &callback); 229 } else { 230 r = nr_async_timer_set_nonzero(timeout, cb, arg, func, l, &callback); 231 } 232 233 if (r) return r; 234 235 if (handle) *handle = callback; 236 237 return 0; 238 } 239 240 int NR_async_schedule(NR_async_cb cb, void* arg, char* func, int l) { 241 // No need to check the thread because we check it next in the 242 // timer set. 243 return NR_async_timer_set(0, cb, arg, func, l, nullptr); 244 } 245 246 int NR_async_timer_cancel(void* handle) { 247 // Check for the handle being nonzero because sometimes we get 248 // no-op cancels that aren't on the STS thread. This can be 249 // non-racy as long as the upper-level code is careful. 250 if (!handle) return 0; 251 252 CheckSTSThread(); 253 254 nrappkitCallback* callback = static_cast<nrappkitCallback*>(handle); 255 callback->Cancel(); 256 257 return 0; 258 }