File locking on simultaneous access [Part 1]

Vấn đề: nhiều process thao tác trên cùng một file, làm thế nào để đảm bảo tại một thời điểm, chỉ có một process được quyền thao tác trên file (đọc file, update nội dung, ghi lại)

Tuy Linux có cung cấp một số cơ chế từ kernel-space để xử lý chuyện này, nhưng khởi đầu tôi coi như mình không biết và tự nghĩ ra một cơ chế khác để xử lý từ mức chương trình (user-space).

Mô tả tóm tắt cơ chế: mỗi process khi cần thao tác trên file file_name sẽ tạo ra 1 file đánh dấu (dạng .lock)
Nếu số .lock file bằng 1 thì bắt đầu quá trình xử lý, còn ko thì xóa file lock mà mình(chính process đó) tạo ra và... đợi đến lượt :D

Implementation thì phức tạp hơn một chút và tôi để các bạn tự luận.

    #!/usr/bin/env python
    """Usage:  python resolve_race_condition.py [process_identifier]
    Sample:    for i in `seq 1 10`; do python resolve_race_condition.py $i & done
    """
    import sys
    import commands
    from random import random
    from time import sleep

    def process_file(fh=None):
        print "Process", sys.argv[1], "manipulating file..."
        sleep(1)  # Gia lap dang thao tac tren file /tmp/ifaces.obj    
        print "Done on process", sys.argv[1]

    if __name__ == "__main__":    
        ## Resolve race-condition
        while True:
            rand = str(random())
            existed = int(commands.getoutput('ls -l /tmp/ifaces.obj.lock* 2>/dev/null | wc -l'))
            if not existed:
                commands.getoutput('touch /tmp/ifaces.obj.lock.'+rand)
                print "Process", sys.argv[1], "created a lock file."
            else:
                print "Process", sys.argv[1], "is waiting for its turn, because lock file(s) existed."
                sleep(float(rand))
                continue

            """If Python-interpreter can come here: the lock-file is not existed, and created;
            but you can't sure about other processes, it may silmultaneously check lock-file
            (and simultaneously see that lock-file does not exist), then it creat a lock-file.

            Therefore, we must re-check below:"""            
            existed = int(commands.getoutput('ls -l /tmp/ifaces.obj.lock* 2>/dev/null | wc -l'))
            if existed >= 2:
                print "No. of lock.* files =", existed
                commands.getoutput('rm -f /tmp/ifaces.obj.lock.'+rand+' 2>/dev/null')
            elif existed == 1:
                print "No. of lock.* files =", existed
                break  # End check-loop, jump to processing part

        ## Now, flag in your hands. Do it!
        process_file()

        # Remove lock-file when finish
        commands.getoutput('rm -f /tmp/ifaces.obj.lock.'+rand+' 2>/dev/null')

Chạy test bằng lệnh:

    for i in `seq 1 10`; do python resolve_race_condition.py $i & done

Với 10 process có nhu cầu xử lý đồng thời trên file /tmp/ifaces.obj, chương trình tốn mất khoảng 13 seconds để hoàn thành. Trong khi thời gian thực tế cần để xử lý là ~10 seconds.
Nguyên do là ở đây ta đã sleep ngẫu nhiên một thời gian trong khoảng [0,1) second, có thể cải thiện bằng sleep(random()/10e3) chẳng hạn.

Cách làm này tuy ko native và nhìn ko pro nhưng lại có cái sướng là có thể chạy trên bất cứ nền tảng nào (Win/Linux/Mac/BSD/...) mnà ko phải sửa code vì ko lệ thuộc vào cơ chế của hệ điều hành.

(...to be continued)