@@ -4,6 +4,7 @@ use crate::RawDeltaTable;
44use deltalake:: storage:: object_store:: { MultipartUpload , PutPayloadMut } ;
55use deltalake:: storage:: { DynObjectStore , ListResult , ObjectStoreError , Path } ;
66use deltalake:: DeltaTableBuilder ;
7+ use parking_lot:: Mutex ;
78use pyo3:: exceptions:: { PyIOError , PyNotImplementedError , PyValueError } ;
89use pyo3:: prelude:: * ;
910use pyo3:: types:: { IntoPyDict , PyBytes , PyType } ;
@@ -519,7 +520,9 @@ impl ObjectInputFile {
519520// TODO the C++ implementation track an internal lock on all random access files, DO we need this here?
520521#[ pyclass( weakref, module = "deltalake._internal" ) ]
521522pub struct ObjectOutputStream {
522- upload : Box < dyn MultipartUpload > ,
523+ // wrap in mutex as rustc says `MultipartUpload` can't be
524+ // shared across threads (it isn't sync)
525+ upload : Mutex < Box < dyn MultipartUpload > > ,
523526 pos : i64 ,
524527 #[ pyo3( get) ]
525528 closed : bool ,
@@ -537,7 +540,7 @@ impl ObjectOutputStream {
537540 ) -> Result < Self , ObjectStoreError > {
538541 let upload = store. put_multipart ( & path) . await ?;
539542 Ok ( Self {
540- upload,
543+ upload : Mutex :: new ( upload ) ,
541544 pos : 0 ,
542545 closed : false ,
543546 mode : "wb" . into ( ) ,
@@ -555,14 +558,15 @@ impl ObjectOutputStream {
555558 }
556559
557560 fn abort ( & mut self ) -> PyResult < ( ) > {
558- rt ( ) . block_on ( self . upload . abort ( ) )
561+ rt ( ) . block_on ( self . upload . lock ( ) . abort ( ) )
559562 . map_err ( PythonError :: from) ?;
560563 Ok ( ( ) )
561564 }
562565
563566 fn upload_buffer ( & mut self ) -> PyResult < ( ) > {
564567 let payload = std:: mem:: take ( & mut self . buffer ) . freeze ( ) ;
565- match rt ( ) . block_on ( self . upload . put_part ( payload) ) {
568+ let res = rt ( ) . block_on ( self . upload . lock ( ) . put_part ( payload) ) ;
569+ match res {
566570 Ok ( _) => Ok ( ( ) ) ,
567571 Err ( err) => {
568572 self . abort ( ) ?;
@@ -580,7 +584,7 @@ impl ObjectOutputStream {
580584 if !self . buffer . is_empty ( ) {
581585 self . upload_buffer ( ) ?;
582586 }
583- match rt ( ) . block_on ( self . upload . complete ( ) ) {
587+ match rt ( ) . block_on ( self . upload . lock ( ) . complete ( ) ) {
584588 Ok ( _) => Ok ( ( ) ) ,
585589 Err ( err) => Err ( PyIOError :: new_err ( err. to_string ( ) ) ) ,
586590 }
0 commit comments