blob: f0140a9fca6a23fd71766b59b558362a8ee23e00 [file]
#
# 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.
import asyncio
import threading
import uuid
from typing import Optional
from dubbo.loggers import loggerFactory
_LOGGER = loggerFactory.get_logger()
def _try_use_uvloop() -> None:
"""
Use uvloop instead of the default asyncio running_loop.
"""
import asyncio
import os
# Check if the operating system.
if os.name == "nt":
# Windows is not supported.
_LOGGER.warning("Unable to use uvloop, because it is not supported on your operating system.")
return
# Try import uvloop.
try:
import uvloop
except ImportError:
# uvloop is not available.
_LOGGER.warning(
"Unable to use uvloop, because it is not installed. You can install it by running `pip install uvloop`."
)
return
# Use uvloop instead of the default asyncio running_loop.
if not isinstance(asyncio.get_event_loop_policy(), uvloop.EventLoopPolicy):
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
# Call the function to try to use uvloop.
_try_use_uvloop()
class EventLoop:
def __init__(self, in_other_tread: bool = True):
self._in_other_tread = in_other_tread
# The event loop to run the asynchronous function.
self._loop = asyncio.new_event_loop()
# The thread to run the event loop.
self._thread: Optional[threading.Thread] = None if in_other_tread else threading.current_thread()
self._started = False
self._stopped = False
# The lock to protect the event loop.
self._lock = threading.Lock()
@property
def loop(self):
"""
Get the event loop.
:return: The event loop.
:rtype: asyncio.AbstractEventLoop
"""
return self._loop
@property
def thread(self) -> Optional[threading.Thread]:
"""
Get the thread of the event loop.
:return: The thread of the event loop. If not yet started, this is None.
:rtype: Optional[threading.Thread]
"""
return self._thread
def check_thread(self) -> bool:
"""
Check if the current thread is the event loop thread.
:return: True if the current thread is the event loop thread, otherwise False.
:rtype: bool
"""
return threading.current_thread().ident == self._thread.ident
@property
def started(self) -> bool:
"""
Check if the event loop is started.
:return: True if the event loop is started, otherwise False.
:rtype: bool
"""
return self._started
@property
def stopped(self) -> bool:
"""
Check if the event loop is stopped.
:return: True if the event loop is stopped, otherwise False.
:rtype: bool
"""
return self._stopped
def start(self) -> None:
"""
Start the asyncio event loop.
"""
if self._started:
return
with self._lock:
self._started = True
self._stopped = False
if self._in_other_tread:
self._start_in_thread()
else:
self._start()
def _start(self) -> None:
"""
Real start the asyncio event loop in current thread.
"""
asyncio.set_event_loop(self._loop)
self._loop.run_forever()
def _start_in_thread(self) -> None:
"""
Real Start the asyncio event loop in a separate thread.
"""
thread_name = f"dubbo-asyncio-loop-{str(uuid.uuid4())}"
thread = threading.Thread(target=self._start, name=thread_name, daemon=True)
thread.start()
self._thread = thread
def stop(self, wait: bool = False) -> None:
"""
Stop the asyncio event loop.
"""
if self._stopped:
return
with self._lock:
signal = threading.Event()
asyncio.run_coroutine_threadsafe(self._stop(signal=signal), self._loop)
# Wait for the running_loop to stop
if wait:
signal.wait()
if self._in_other_tread:
self._thread.join()
self._stopped = True
self._started = False
async def _stop(self, signal: threading.Event) -> None:
"""
Real stop the asyncio event loop.
"""
# Cancel all tasks
tasks = [task for task in asyncio.all_tasks(self._loop) if task is not asyncio.current_task()]
for task in tasks:
task.cancel()
await asyncio.gather(*tasks, return_exceptions=True)
# Stop the event running_loop
self._loop.stop()
# Set the signal
signal.set()