OK, updated code according to Owen's idea (and with the user-handle concept):
Code: Select all
struct mutex
{
int handle;
int counter;
short int val;
short int owner;
};
; create new mutex
; returns handle in ebx
create:
push eax
push ecx
push edx
push edi
mov edx,OFFSET handle_tab
mov edi,edx
mov ecx,MAX_SECTIONS
repnz scasd ; search for free handle
stc
jnz create_done ; no free handles
mov ebx,MAX_SECTIONS
sub ebx,ecx
push ebx ; ebx now is the handle (1-based index)
dec ebx
sub edi,4 ; edi is the handle table index
mov eax,12
mul ebx
add eax,4 * MAX_SECTIONS
mov edx,eax
add edx,OFFSET handle_tab ; edx is the mutex entry itself
mov [edi],edx ; save address to mutex in index array
mov [edx].handle,0 ; initialize mutex
mov [edx].val,-1
mov [edx].counter,0
mov [edx].owner,0
pop ebx
clc
create_done:
pop edi
pop edx
pop ecx
pop eax
ret
; free mutex
; ebx is handle
free:
push edx
sub ebx,1
jc free_done
cmp ebx,MAX_SECTIONS ; check that hande is in range
jae free_done
shl ebx,2
add ebx,OFFSET handle_tab
xor eax,eax
xchg eax,[ebx] ; mark handle as free
or eax,eax
jz free_done
mov ebx,eax
mov eax,[ebx].handle ; check if a kernel contention handle has been allocated
or eax,eax
jz free_done
FreeFutex(ebx) ; free kernel side handle
free_done:
xor ebx,ebx
pop edx
ret
; lock mutex
; ebx is handle
lock:
push eax
push edx
sub ebx,1
jc lock_done ; invalid zero handle
cmp ebx,MAX_SECTIONS
jae lock_done ; index out of range
shl ebx,2
add ebx,OFFSET handle_tab
mov ebx,[ebx]
or ebx,ebx
jz lock_done ; not allocated
str ax
cmp ax,[ebx].owner
jne lock_do ; already owned?
inc [ebx].counter
jmp lock_done
lock_do:
lock add [ebx].val,1
jc lock_take
mov eax,1
xchg ax,[ebx].val ; double check so it isn't free, and avoid the "roll-over" problem
cmp ax,-1
jne lock_block
lock_take:
str ax ; set as owner
mov [ebx].owner,ax
mov [ebx].counter,1
jmp lock_done
lock_block:
RdosAcquireFutex(ebx) ; block
lock_done:
pop ebx
pop eax
ret
; unlock mutex
; ebx is handle
unlock:
push eax
push ebx
sub ebx,1
jc unlock_done ; invalid zero handle
cmp ebx,MAX_SECTIONS
jae unlock_done ; index out of range
shr ebx,2
add ebx,OFFSET handle_tab
mov ebx,[ebx]
or ebx,ebx
jz unlock_done ; not allocated
str ax
cmp ax,[ebx].owner
jne unlock_done ; check that owner is correct
sub [ebx].counter,1
jnz unlock_done
mov [ebx].owner,0
lock sub [ebx].val,1
jc unlock_done ; go if not contended
mov [ebx].val,-1 ; temporarily set to free to handle race condition with wakeup
RdosReleaseFutex(ebx)
unlock_done:
pop ebx
pop eax
ret
Edit: When the acquire call returns from kernel, the kernel can ensure that the mutex is always owned, and thus there is no need to check after the blocking call returns.
Edit2: In order to handle race conditions it is necesary to set the mutex to free before doing the release futex syscall.