@@ -706,37 +706,68 @@ $$ LANGUAGE plpgsql;
706706-- Similarly, for memory allocation, use transactions and more verbose errors
707707CREATE OR REPLACE FUNCTION allocate_memory (user_id INTEGER , process_id INTEGER , segment_size INTEGER ) RETURNS VOID AS $$
708708DECLARE
709- mem_seg RECORD;
709+ mem_seg memory_segments%ROWTYPE;
710+ updated_seg memory_segments%ROWTYPE;
711+ locked BOOLEAN := FALSE;
710712BEGIN
711713 IF NOT check_permission(user_id, ' memory' , ' allocate' ) THEN
712714 RAISE EXCEPTION ' User % does not have permission to allocate memory' , user_id;
713715 END IF;
714716
715717 BEGIN
716- SELECT * INTO mem_seg FROM memory_segments
717- WHERE allocated = FALSE AND size >= segment_size
718- ORDER BY size
719- LIMIT 1 ;
720-
721- IF NOT FOUND THEN
722- RAISE EXCEPTION ' No suitable memory segment available of size %' , segment_size;
723- END IF;
724-
725- -- Transaction block for atomic allocation
726- BEGIN
727- PERFORM pg_advisory_lock(1 ); -- simulate locking, ensure no other transaction interferes
728- UPDATE memory_segments SET allocated = TRUE, allocated_to = process_id WHERE id = mem_seg .id ;
729- INSERT INTO process_memory (process_id, segment_id) VALUES (process_id, mem_seg .id );
718+ PERFORM pg_advisory_lock(1 ); -- simulate locking, ensure no other transaction interferes
719+ locked := TRUE;
720+
721+ LOOP
722+ SELECT *
723+ INTO mem_seg
724+ FROM memory_segments
725+ WHERE allocated = FALSE AND size >= segment_size
726+ ORDER BY size
727+ LIMIT 1
728+ FOR UPDATE SKIP LOCKED;
729+
730+ IF NOT FOUND THEN
731+ IF locked THEN
732+ PERFORM pg_advisory_unlock(1 );
733+ locked := FALSE;
734+ END IF;
735+ RAISE EXCEPTION ' No suitable memory segment available of size %' , segment_size;
736+ END IF;
737+
738+ UPDATE memory_segments
739+ SET allocated = TRUE,
740+ allocated_to = process_id
741+ WHERE id = mem_seg .id
742+ AND allocated = FALSE
743+ RETURNING * INTO updated_seg;
744+
745+ IF FOUND THEN
746+ EXIT;
747+ END IF;
748+
749+ IF locked THEN
750+ PERFORM pg_advisory_unlock(1 );
751+ locked := FALSE;
752+ END IF;
753+ RAISE EXCEPTION ' Memory segment % was allocated concurrently' , mem_seg .id ;
754+ END LOOP;
755+
756+ INSERT INTO process_memory (process_id, segment_id) VALUES (process_id, updated_seg .id );
757+
758+ IF locked THEN
730759 PERFORM pg_advisory_unlock(1 );
731- EXCEPTION WHEN others THEN
732- PERFORM pg_advisory_unlock(1 );
733- RAISE;
734- END;
760+ locked := FALSE;
761+ END IF;
735762
736763 -- Log the allocation
737- PERFORM log_memory_action(process_id, ' Memory allocated: segment ' || mem_seg .id , user_id, mem_seg .id );
764+ PERFORM log_memory_action(process_id, ' Memory allocated: segment ' || updated_seg .id , user_id, updated_seg .id );
738765
739766 EXCEPTION WHEN others THEN
767+ IF locked THEN
768+ PERFORM pg_advisory_unlock(1 );
769+ locked := FALSE;
770+ END IF;
740771 RAISE EXCEPTION ' Error allocating memory: %' , SQLERRM;
741772 END;
742773END;
0 commit comments