XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

perf(browser): replace file lock with asyncio lock for browser concurrency

470b06a3
Anand Mall <anand@example.com>
提交于

代码差异

1 个文件 +45 -13
Modified g4f/requests/__init__.py +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