返回提交历史
Modified
g4f/requests/__init__.py
+45
-13
XFEstudio/gpt4free
perf(browser): replace file lock with asyncio lock for browser concurrency
470b06a3
代码差异
1 个文件
+45
-13
@@ -262,6 +262,15 @@ def merge_cookies(cookies: Iterator[Morsel], response: Response) -> Cookies:
262
262
return cookies
263
263
264
264
265
_browser_locks: dict[str, asyncio.Lock] = {}
266
267
268
def get_browser_lock(user_data_dir: str) -> asyncio.Lock:
269
if user_data_dir not in _browser_locks:
270
_browser_locks[user_data_dir] = asyncio.Lock()
271
return _browser_locks[user_data_dir]
272
273
265
274
def set_browser_executable_path(browser_executable_path: str):
266
275
BrowserConfig.browser_executable_path = browser_executable_path
267
276
@@ -300,36 +309,51 @@ async def get_nodriver(
300
309
if not os.path.exists(browser_executable_path):
301
310
browser_executable_path = None
302
311
debug.log(f"Browser executable path: {browser_executable_path}")
312
313
browser_lock = get_browser_lock(str(user_data_dir)) if user_data_dir else None
314
if browser_lock is not None:
315
try:
316
await asyncio.wait_for(browser_lock.acquire(), timeout=timeout)
317
except asyncio.TimeoutError:
318
raise TimeoutError("Nodriver is already in use, please try again later.")
319
303
320
lock_file = Path(get_cookies_dir()) / ".browser_is_open"
304
321
if user_data_dir:
305
322
lock_file.parent.mkdir(exist_ok=True)
306
# Implement a short delay (milliseconds) to prevent race conditions.
307
await asyncio.sleep(0.1 * random.randint(0, 50))
308
323
if lock_file.exists():
309
opend_at = float(lock_file.read_text())
310
time_open = time.time() - opend_at
311
if timeout * 2 > time_open:
324
try:
325
opend_at = float(lock_file.read_text().strip())
326
time_open = time.time() - opend_at
327
except (ValueError, OSError):
328
time_open = float("inf")
329
330
if time_open < timeout * 2:
312
331
debug.log(
313
f"Nodriver: Browser is already in use since {time_open} secs."
332
f"Nodriver: Browser is already in use since {time_open:.1f} secs."
314
333
)
315
334
debug.log("Lock file:", lock_file)
316
for idx in range(timeout):
335
for idx in range(int(timeout)):
317
336
if lock_file.exists():
318
await asyncio.sleep(1)
337
await asyncio.sleep(0.5)
319
338
else:
320
339
break
321
if idx == timeout - 1:
340
if idx == int(timeout) - 1:
322
341
debug.log("Timeout reached, nodriver is still in use.")
342
if browser_lock and browser_lock.locked():
343
browser_lock.release()
323
344
raise TimeoutError(
324
345
"Nodriver is already in use, please try again later."
325
346
)
326
347
else:
327
348
debug.log(
328
f"Nodriver: Browser was opened {time_open} secs ago, closing it."
349
f"Nodriver: Stale browser lock detected ({time_open:.1f} secs ago), releasing."
329
350
)
330
351
await BrowserConfig.stop_browser()
331
352
lock_file.unlink(missing_ok=True)
332
lock_file.write_text(str(time.time()))
353
try:
354
lock_file.write_text(str(time.time()))
355
except OSError:
356
pass
333
357
debug.log(f"Open nodriver with user_dir: {user_data_dir}")
334
358
try:
335
359
browser_args = kwargs.pop("browser_args", None) or ["--no-sandbox"]
@@ -347,8 +371,14 @@ async def get_nodriver(
347
371
connection_timeout=BrowserConfig.connection_timeout,
348
372
**kwargs,
349
373
)
350
except FileNotFoundError as e:
351
raise MissingRequirementsError(e)
374
except Exception as e:
375
if user_data_dir:
376
lock_file.unlink(missing_ok=True)
377
if browser_lock and browser_lock.locked():
378
browser_lock.release()
379
if isinstance(e, FileNotFoundError):
380
raise MissingRequirementsError(e)
381
raise
352
382
353
383
async def on_stop():
354
384
try:
@@ -359,6 +389,8 @@ async def get_nodriver(
359
389
finally:
360
390
if user_data_dir:
361
391
lock_file.unlink(missing_ok=True)
392
if browser_lock and browser_lock.locked():
393
browser_lock.release()
362
394
363
395
BrowserConfig.stop_browser = on_stop
364
396
return browser, on_stop