1212
1313import os as _os
1414import sys as _sys
15- from collections .abc import Generator
15+ import threading as _threading
16+ import numpy .typing
17+ from os import SEEK_SET , SEEK_CUR , SEEK_END
1618from ctypes .util import find_library as _find_library
1719from os import SEEK_CUR , SEEK_END , SEEK_SET
1820from typing import Any , BinaryIO , Final , Literal , TypeAlias , overload
@@ -1307,6 +1309,13 @@ def close(self) -> None:
13071309 self ._file = None
13081310 _error_check (err )
13091311
1312+ # sf_error(NULL) returns a global (non-thread-safe) error code.
1313+ # When an sf_open* call fails (returns NULL), we must call
1314+ # sf_error(NULL) to retrieve the error code, but another thread
1315+ # may clear the global error between our open and our sf_error.
1316+ # This lock serialises the open+sf_error(NULL) pair to prevent that.
1317+ _open_lock = _threading .Lock ()
1318+
13101319 def _open (self , file , mode_int , closefd ):
13111320 """Call the appropriate sf_open*() function from libsndfile."""
13121321 if isinstance (file , (str , bytes )):
@@ -1322,18 +1331,26 @@ def _open(self, file, mode_int, closefd):
13221331 openfunction = _snd .sf_wchar_open
13231332 else :
13241333 file = file .encode (_sys .getfilesystemencoding ())
1325- file_ptr = openfunction (file , mode_int , self ._info )
1334+ with self ._open_lock :
1335+ file_ptr = openfunction (file , mode_int , self ._info )
1336+ if file_ptr == _ffi .NULL :
1337+ err = _snd .sf_error (file_ptr )
1338+ raise LibsndfileError (err , prefix = "Error opening {0!r}: " .format (self .name ))
13261339 elif isinstance (file , int ):
1327- file_ptr = _snd .sf_open_fd (file , mode_int , self ._info , closefd )
1340+ with self ._open_lock :
1341+ file_ptr = _snd .sf_open_fd (file , mode_int , self ._info , closefd )
1342+ if file_ptr == _ffi .NULL :
1343+ err = _snd .sf_error (file_ptr )
1344+ raise LibsndfileError (err , prefix = "Error opening {0!r}: " .format (self .name ))
13281345 elif _has_virtual_io_attrs (file , mode_int ):
1329- file_ptr = _snd .sf_open_virtual (self ._init_virtual_io (file ),
1330- mode_int , self ._info , _ffi .NULL )
1346+ with self ._open_lock :
1347+ file_ptr = _snd .sf_open_virtual (self ._init_virtual_io (file ),
1348+ mode_int , self ._info , _ffi .NULL )
1349+ if file_ptr == _ffi .NULL :
1350+ err = _snd .sf_error (file_ptr )
1351+ raise LibsndfileError (err , prefix = "Error opening {0!r}: " .format (self .name ))
13311352 else :
1332- raise TypeError (f"Invalid file: { self .name !r} " )
1333- if file_ptr == _ffi .NULL :
1334- # get the actual error code
1335- err = _snd .sf_error (file_ptr )
1336- raise LibsndfileError (err , prefix = f"Error opening { self .name !r} : " )
1353+ raise TypeError ("Invalid file: {0!r}" .format (self .name ))
13371354 if mode_int == _snd .SFM_WRITE :
13381355 # Due to a bug in libsndfile version <= 1.0.25, frames != 0
13391356 # when opening a named pipe in SFM_WRITE mode.
0 commit comments