Fix hanging bug by adding manager
This commit is contained in:
parent
ad1b2a20ef
commit
2f4d203446
10 changed files with 40 additions and 30 deletions
|
|
@ -6,6 +6,6 @@ from .get_sentences import get_sentences
|
||||||
from .language import english_name_of_language, is_english, predict_language
|
from .language import english_name_of_language, is_english, predict_language
|
||||||
from .logger import get_logger
|
from .logger import get_logger
|
||||||
from .match_names import match_names
|
from .match_names import match_names
|
||||||
from .parallel_map import parallel_map, threaded_parallel_map
|
from .parallel_map import WorkerException, parallel_map, threaded_parallel_map
|
||||||
from .unchunk import unchunk
|
from .unchunk import unchunk
|
||||||
from .unique import unique
|
from .unique import unique
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
from .parallel_map import parallel_map
|
from .parallel_map import parallel_map
|
||||||
from .threaded_parallel_map import threaded_parallel_map
|
from .threaded_parallel_map import threaded_parallel_map
|
||||||
|
from .worker_exception import WorkerException
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ from typing import Dict, Iterable, TypeVar, Union
|
||||||
|
|
||||||
from ..chunk import chunk
|
from ..chunk import chunk
|
||||||
from ..logger import get_logger
|
from ..logger import get_logger
|
||||||
|
from .worker_exception import WorkerException
|
||||||
|
|
||||||
logger = get_logger("parallel_map")
|
logger = get_logger("parallel_map")
|
||||||
|
|
||||||
|
|
@ -36,7 +37,7 @@ def manage_communication(
|
||||||
is_iteration_over = True
|
is_iteration_over = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not ignore_exceptions:
|
if not ignore_exceptions:
|
||||||
raise e
|
raise
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Exception {e} encountered in input, traceback:\n{traceback.format_exc()}"
|
f"Exception {e} encountered in input, traceback:\n{traceback.format_exc()}"
|
||||||
|
|
@ -49,7 +50,7 @@ def manage_communication(
|
||||||
if exception is not None:
|
if exception is not None:
|
||||||
e, tb = exception
|
e, tb = exception
|
||||||
if not ignore_exceptions:
|
if not ignore_exceptions:
|
||||||
raise e
|
raise WorkerException from e
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Exception {e} encountered in worker, traceback:\n{tb}"
|
f"Exception {e} encountered in worker, traceback:\n{tb}"
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import traceback
|
||||||
from typing import Callable, Iterable, TypeVar
|
from typing import Callable, Iterable, TypeVar
|
||||||
|
|
||||||
from ..logger import get_logger
|
from ..logger import get_logger
|
||||||
|
from .worker_exception import WorkerException
|
||||||
|
|
||||||
logger = get_logger("parallel_map")
|
logger = get_logger("parallel_map")
|
||||||
|
|
||||||
|
|
@ -21,15 +22,15 @@ def manage_serial(
|
||||||
yield function(v)
|
yield function(v)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if not ignore_exceptions:
|
if not ignore_exceptions:
|
||||||
raise e
|
raise WorkerException from e
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
f"Exception {e} encountered in input, traceback:\n{traceback.format_exc()}"
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
if not ignore_exceptions:
|
|
||||||
raise e
|
|
||||||
else:
|
else:
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Exception {e} encountered in worker, traceback:\n{traceback.format_exc()}"
|
f"Exception {e} encountered in worker, traceback:\n{traceback.format_exc()}"
|
||||||
)
|
)
|
||||||
|
except Exception as e:
|
||||||
|
if not ignore_exceptions:
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f"Exception {e} encountered in input, traceback:\n{traceback.format_exc()}"
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import multiprocessing as mp
|
||||||
import queue
|
import queue
|
||||||
import threading
|
import threading
|
||||||
import traceback
|
import traceback
|
||||||
from time import sleep
|
|
||||||
from typing import Callable, Union
|
from typing import Callable, Union
|
||||||
|
|
||||||
import dill
|
import dill
|
||||||
|
|
@ -19,7 +18,7 @@ def mapper_function(
|
||||||
map_function = dill.loads(map_function)
|
map_function = dill.loads(map_function)
|
||||||
|
|
||||||
last_chunk = None
|
last_chunk = None
|
||||||
while not should_stop.is_set():
|
while not should_stop.wait(0.1):
|
||||||
if last_chunk is None:
|
if last_chunk is None:
|
||||||
try:
|
try:
|
||||||
input_chunk = input_queue.get_nowait()
|
input_chunk = input_queue.get_nowait()
|
||||||
|
|
@ -32,16 +31,13 @@ def mapper_function(
|
||||||
exception = e, traceback.format_exc()
|
exception = e, traceback.format_exc()
|
||||||
last_chunk.append((i, result, exception))
|
last_chunk.append((i, result, exception))
|
||||||
except queue.Empty:
|
except queue.Empty:
|
||||||
sleep(
|
pass
|
||||||
0.1
|
|
||||||
) # input_queue.get(0.1) would hang in some cases with multiprocessing
|
if last_chunk is not None:
|
||||||
else:
|
|
||||||
try:
|
try:
|
||||||
output_queue.put_nowait(last_chunk)
|
output_queue.put_nowait(last_chunk)
|
||||||
last_chunk = None
|
last_chunk = None
|
||||||
except queue.Full:
|
except queue.Full:
|
||||||
sleep(
|
pass
|
||||||
0.1
|
|
||||||
) # output_queue.put(0.1) would hang in some cases with multiprocessing
|
|
||||||
except (KeyboardInterrupt, BrokenPipeError):
|
except (KeyboardInterrupt, BrokenPipeError):
|
||||||
pass
|
pass
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ from .get_config import get_config
|
||||||
from .manage_communication import manage_communication
|
from .manage_communication import manage_communication
|
||||||
from .manage_serial import manage_serial
|
from .manage_serial import manage_serial
|
||||||
from .mapper_function import mapper_function
|
from .mapper_function import mapper_function
|
||||||
|
from .worker_exception import WorkerException
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
V = TypeVar("V")
|
V = TypeVar("V")
|
||||||
|
|
@ -87,13 +88,17 @@ def parallel_map(
|
||||||
ignore_exceptions=ignore_exceptions,
|
ignore_exceptions=ignore_exceptions,
|
||||||
)
|
)
|
||||||
|
|
||||||
ctx = mp.get_context("spawn")
|
ctx = (
|
||||||
|
mp.get_context("fork")
|
||||||
|
if "fork" in mp.get_all_start_methods()
|
||||||
|
else mp.get_context("spawn")
|
||||||
|
)
|
||||||
ctx.freeze_support()
|
ctx.freeze_support()
|
||||||
|
manager = ctx.Manager()
|
||||||
|
input_queue = manager.Queue(config.concurrency * 2)
|
||||||
|
output_queue = manager.Queue(config.concurrency * 2)
|
||||||
|
|
||||||
input_queue = ctx.Queue(config.concurrency * 2)
|
|
||||||
output_queue = ctx.Queue(config.concurrency * 2)
|
|
||||||
should_stop = ctx.Event()
|
should_stop = ctx.Event()
|
||||||
|
|
||||||
serialized_map_function = dill.dumps(function, byref=True, recurse=True)
|
serialized_map_function = dill.dumps(function, byref=True, recurse=True)
|
||||||
|
|
||||||
processes = [
|
processes = [
|
||||||
|
|
@ -124,13 +129,17 @@ def parallel_map(
|
||||||
ignore_exceptions=ignore_exceptions,
|
ignore_exceptions=ignore_exceptions,
|
||||||
)
|
)
|
||||||
should_stop.set()
|
should_stop.set()
|
||||||
except:
|
except WorkerException:
|
||||||
|
should_stop.set()
|
||||||
|
raise
|
||||||
|
except Exception:
|
||||||
for p in processes:
|
for p in processes:
|
||||||
p.terminate()
|
p.terminate()
|
||||||
|
p.kill()
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
for p in processes:
|
for p in processes:
|
||||||
p.join() # terminated processes have to be joined else they remain zombies
|
p.join() # terminated processes have to be joined else they remain zombies
|
||||||
p.close()
|
p.close()
|
||||||
input_queue.close()
|
|
||||||
output_queue.close()
|
manager.shutdown()
|
||||||
|
|
|
||||||
|
|
@ -118,4 +118,4 @@ def threaded_parallel_map(
|
||||||
)
|
)
|
||||||
should_stop.set()
|
should_stop.set()
|
||||||
for t in threads:
|
for t in threads:
|
||||||
t.join()
|
t.join(1)
|
||||||
|
|
|
||||||
2
src/great_ai/utilities/parallel_map/worker_exception.py
Normal file
2
src/great_ai/utilities/parallel_map/worker_exception.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
class WorkerException(Exception):
|
||||||
|
pass
|
||||||
|
|
@ -2,7 +2,7 @@ import unittest
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from src.great_ai.utilities import parallel_map
|
from src.great_ai.utilities import WorkerException, parallel_map
|
||||||
|
|
||||||
COUNT = int(1e5) + 3
|
COUNT = int(1e5) + 3
|
||||||
|
|
||||||
|
|
@ -91,10 +91,10 @@ class TestParallelMap(unittest.TestCase):
|
||||||
def oh_no(_):
|
def oh_no(_):
|
||||||
raise ValueError("hi")
|
raise ValueError("hi")
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(WorkerException):
|
||||||
list(parallel_map(oh_no, range(COUNT), concurrency=2))
|
list(parallel_map(oh_no, range(COUNT), concurrency=2))
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(WorkerException):
|
||||||
list(parallel_map(oh_no, range(COUNT), concurrency=1))
|
list(parallel_map(oh_no, range(COUNT), concurrency=1))
|
||||||
|
|
||||||
def test_ignore_worker_process_exception(self) -> None:
|
def test_ignore_worker_process_exception(self) -> None:
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import unittest
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from src.great_ai.utilities import threaded_parallel_map
|
from src.great_ai.utilities import WorkerException, threaded_parallel_map
|
||||||
|
|
||||||
COUNT = int(1e5) + 3
|
COUNT = int(1e5) + 3
|
||||||
|
|
||||||
|
|
@ -91,10 +91,10 @@ class TestParallelMap(unittest.TestCase):
|
||||||
def oh_no(_):
|
def oh_no(_):
|
||||||
raise ValueError("hi")
|
raise ValueError("hi")
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(WorkerException):
|
||||||
list(threaded_parallel_map(oh_no, range(COUNT), concurrency=2))
|
list(threaded_parallel_map(oh_no, range(COUNT), concurrency=2))
|
||||||
|
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(WorkerException):
|
||||||
list(threaded_parallel_map(oh_no, range(COUNT), concurrency=1))
|
list(threaded_parallel_map(oh_no, range(COUNT), concurrency=1))
|
||||||
|
|
||||||
def test_ignore_worker_worker_exception(self) -> None:
|
def test_ignore_worker_worker_exception(self) -> None:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue