@@ -737,6 +737,8 @@ impl Clone for ContainerPool {
737737 max_size : self . max_size ,
738738 min_size : self . min_size ,
739739 enabled : self . enabled ,
740+ // Clone the semaphore
741+ semaphore : Arc :: clone ( & self . semaphore ) ,
740742 }
741743 }
742744}
@@ -746,6 +748,8 @@ pub struct ContainerPool {
746748 pub max_size : usize ,
747749 pub min_size : usize ,
748750 pub enabled : bool ,
751+ /// Semaphore to limit concurrent container creation
752+ semaphore : Arc < tokio:: sync:: Semaphore > ,
749753}
750754
751755impl ContainerPool {
@@ -769,6 +773,8 @@ impl ContainerPool {
769773 max_size,
770774 min_size,
771775 enabled,
776+ // Semaphore with max_size permits to limit concurrent container creation
777+ semaphore : Arc :: new ( tokio:: sync:: Semaphore :: new ( max_size) ) ,
772778 }
773779 }
774780
@@ -810,51 +816,60 @@ impl ContainerPool {
810816 Ok ( ( ) )
811817 }
812818
819+ /// Acquire a container from the pool
820+ /// Uses semaphore to prevent race conditions during container creation
813821 pub async fn acquire ( & self , group_folder : & str ) -> Option < PooledContainer > {
814822 if !self . enabled {
815823 return None ;
816824 }
817825
818- let mut containers = self . containers . lock ( ) . await ;
819-
820- // First, try to find an available container
821- for ( id, container) in containers. iter_mut ( ) {
822- if !container. in_use && container. group_folder == group_folder {
823- container. in_use = true ;
824- return Some ( PooledContainer {
825- inner : Arc :: new ( Mutex :: new ( container. clone ( ) ) ) ,
826- pool : Arc :: new ( self . clone ( ) ) ,
827- } ) ;
826+ // Try to find an available container first (fast path)
827+ // No permit needed - we're reusing an existing container
828+ {
829+ let mut containers = self . containers . lock ( ) . await ;
830+ for ( id, container) in containers. iter_mut ( ) {
831+ if !container. in_use && container. group_folder == group_folder {
832+ container. in_use = true ;
833+ return Some ( PooledContainer {
834+ inner : Arc :: new ( Mutex :: new ( container. clone ( ) ) ) ,
835+ pool : Arc :: new ( self . clone ( ) ) ,
836+ } ) ;
837+ }
828838 }
829839 }
830840
831- // Try to create a new container if under limit
832- // Double-check after acquiring lock to prevent race condition
841+ // Need to create a new container - use semaphore to limit concurrency
842+ // Acquire permit - will wait if no permits available
843+ let _permit = match self . semaphore . acquire ( ) . await {
844+ Ok ( p) => p,
845+ Err ( _) => return None , // Pool was closed
846+ } ;
847+
848+ // We have a permit, proceed with container creation
849+ let mut containers = self . containers . lock ( ) . await ;
850+
851+ // Check if we can still create a new container (another thread might have created one)
833852 if containers. len ( ) < self . max_size {
834853 let new_id = format ! ( "{}{}" , CONTAINER_NAME_PREFIX , containers. len( ) ) ;
835854 let new_group_folder = group_folder. to_string ( ) ;
836855
837- // Check again to avoid race condition
838- if containers. len ( ) < self . max_size {
839- // Release lock before async operation
840- drop ( containers) ;
856+ // Release lock before async operation
857+ drop ( containers) ;
841858
842- if start_pooled_container ( & new_id, & new_group_folder)
843- . await
844- . is_ok ( )
845- {
859+ // Start the container
860+ match start_pooled_container ( & new_id, & new_group_folder) . await {
861+ Ok ( _) => {
846862 let mut containers = self . containers . lock ( ) . await ;
847863
848- // Final check after acquiring lock
864+ // Double- check size after async operation
849865 if containers. len ( ) < self . max_size {
850- containers. insert (
851- new_id. clone ( ) ,
852- PooledContainerInner {
853- container_id : new_id. clone ( ) ,
854- group_folder : new_group_folder. clone ( ) ,
855- in_use : true ,
856- } ,
857- ) ;
866+ let inner = PooledContainerInner {
867+ container_id : new_id. clone ( ) ,
868+ group_folder : new_group_folder. clone ( ) ,
869+ in_use : true ,
870+ } ;
871+ containers. insert ( new_id. clone ( ) , inner) ;
872+
858873 return Some ( PooledContainer {
859874 inner : Arc :: new ( Mutex :: new ( PooledContainerInner {
860875 container_id : new_id,
@@ -865,12 +880,20 @@ impl ContainerPool {
865880 } ) ;
866881 }
867882 }
883+ Err ( _) => {
884+ // Container creation failed
885+ }
868886 }
869887 }
870888
889+ // Failed to create container - permit will be released when _permit is dropped
871890 None
872891 }
873892
893+ /// Release a container back to the pool
894+ /// Note: This is typically called automatically when PooledContainer is dropped
895+ /// Kept for manual release if needed
896+ #[ allow( dead_code) ]
874897 pub async fn release ( & self , container_id : & str ) {
875898 if !self . enabled {
876899 return ;
0 commit comments