Issue #21326: Add a new is_closed() method to asyncio.BaseEventLoop

Add BaseEventLoop._closed attribute and use it to check if the event loop was
closed or not, instead of checking different attributes in each subclass of
BaseEventLoop.

run_forever() and run_until_complete() methods now raise a RuntimeError('Event loop is
closed') exception if the event loop was closed.

BaseProactorEventLoop.close() now also cancels "accept futures".
diff --git a/Lib/asyncio/base_events.py b/Lib/asyncio/base_events.py
index 1c7073c..5ee21d1 100644
--- a/Lib/asyncio/base_events.py
+++ b/Lib/asyncio/base_events.py
@@ -119,6 +119,7 @@
 class BaseEventLoop(events.AbstractEventLoop):
 
     def __init__(self):
+        self._closed = False
         self._ready = collections.deque()
         self._scheduled = []
         self._default_executor = None
@@ -128,6 +129,11 @@
         self._exception_handler = None
         self._debug = False
 
+    def __repr__(self):
+        return ('<%s running=%s closed=%s debug=%s>'
+                % (self.__class__.__name__, self.is_running(),
+                   self.is_closed(), self.get_debug()))
+
     def _make_socket_transport(self, sock, protocol, waiter=None, *,
                                extra=None, server=None):
         """Create socket transport."""
@@ -173,8 +179,13 @@
         """Process selector events."""
         raise NotImplementedError
 
+    def _check_closed(self):
+        if self._closed:
+            raise RuntimeError('Event loop is closed')
+
     def run_forever(self):
         """Run until stop() is called."""
+        self._check_closed()
         if self._running:
             raise RuntimeError('Event loop is running.')
         self._running = True
@@ -198,6 +209,7 @@
 
         Return the Future's result, or raise its exception.
         """
+        self._check_closed()
         future = tasks.async(future, loop=self)
         future.add_done_callback(_raise_stop_error)
         self.run_forever()
@@ -222,6 +234,9 @@
         This clears the queues and shuts down the executor,
         but does not wait for the executor to finish.
         """
+        if self._closed:
+            return
+        self._closed = True
         self._ready.clear()
         self._scheduled.clear()
         executor = self._default_executor
@@ -229,6 +244,10 @@
             self._default_executor = None
             executor.shutdown(wait=False)
 
+    def is_closed(self):
+        """Returns True if the event loop was closed."""
+        return self._closed
+
     def is_running(self):
         """Returns running status of event loop."""
         return self._running