blob: 1a12d42785544aa628738380ac313c388fe1fdc4 [file] [log] [blame]
/*
* Copyright (c) 2018, Cornell University
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* Neither the name of Cornell University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
//------------------------------------------------------------------------
// sysfutex_d tests FUTEX_WAKE_OP functionalities of futex system call:
// - make a thread wait on a variable
// - atomically wake up a thread waiting on a variable and perform
// a operation on a value
//------------------------------------------------------------------------
#include "riscv_test.h"
#include "test_macros.h"
#include "test_macros_mt_ecall.h"
RVTEST_RV64U
RVTEST_CODE_BEGIN
#define NUM_THREADS 1
#define LOOP_COUNT 1000
//------------------------------------------------------------------------
// Master thread creates new threads, call _master function, waits for all
// threads to complete, deallocates threads and checks result
//------------------------------------------------------------------------
li a0, NUM_THREADS
call _create_threads
la t6, n_worker_threads
ld a0, (t6)
beqz a0, _fail // exit if there's no worker thread
call _master_work
la t6, n_worker_threads
ld a0, (t6)
call _join
la t6, n_worker_threads
ld a0, (t6)
call _check
la t6, n_worker_threads
ld a0, (t6)
call _delete_threads
li a0, SUCCESS
RVTEST_CODE_END
//------------------------------------------------------------------------
// master_work function executed by the parent/master thread
//
// Wake up thread(s) waiting on futex_X and then wait on futex_Y in a
// loop. Also atomically modify futex_Z during the wake-up.
//------------------------------------------------------------------------
_master_work:
mv s0, ra // save return address
li t0, LOOP_COUNT
la t1, count_master
la t3, count_Z
1:
// futex(futex_X, FUTEX_WAKE_OP, 1, val2, futex_Z, val3 )
la a0, futex_X
li a1, FUTEX_WAKE_OP
li a2, 1 // wake up at most 1 thread
li a3, 0 // should not perform the second wake up
la a4, futex_Z // add 1 to futex_Z each time
li a5, FUTEX_OP(FUTEX_OP_ADD, 1, FUTEX_OP_CMP_LT, 0)
li a7, SYSCALL_FUTEX
ecall
// increment count_Z (should equals to futex_Z)
ld t4, (t3)
addi t4, t4, 1
sd t4, (t3)
// keep waking up until at least one thread is waken up
beqz a0, 1b
// increment count_master
ld t2, (t1)
addi t2, t2, 1
sd t2, (t1)
// futex(futex_Y, FUTEX_WAIT_PRIVATE, 0)
la a0, futex_Y
li a1, FUTEX_WAIT_PRIVATE
li a2, 0 // expected val of futex_Y
li a7, SYSCALL_FUTEX
ecall
// decrement t0
addi t0, t0, -1
bnez t0, 1b
// restore return address and return
mv ra, s0
ret
//------------------------------------------------------------------------
// mt_test function executed by child threads
//
// Wait on futex_X and then wake up threads waiting on futex_Y in a loop
//------------------------------------------------------------------------
_mt_test:
li t0, LOOP_COUNT
la t1, count_child
1:
// futex(futex_X, FUTEX_WAIT_PRIVATE, 1)
la a0, futex_X
li a1, FUTEX_WAIT_PRIVATE
li a2, 0 // expected val of futex_X
li a7, SYSCALL_FUTEX
ecall
// increment count_child
ld t2, (t1)
addi t2, t2, 1
sd t2, (t1)
2:
// futex(futex_Y, FUTEX_WAKE_PRIVATE, 0)
la a0, futex_Y
li a1, FUTEX_WAKE_PRIVATE
li a2, 1 // wake up at most 1 thread
li a7, SYSCALL_FUTEX
ecall
// keep waking up until at least one thread is waken up
beqz a0, 2b
// decrement t0
addi t0, t0, -1
bnez t0, 1b
RVTEST_CODE_END
//------------------------------------------------------------------------
// _check:
// Each thread should do LOOP_COUNT iterations
//------------------------------------------------------------------------
_check:
la t0, count_master
la t1, count_child
li t2, LOOP_COUNT
la t3, count_Z
la t4, futex_Z
ld t0, (t0)
bne t0, t2, _fail
ld t1, (t1)
bne t1, t2, _fail
ld t3, (t3)
ld t4, (t4)
bne t3, t4, _fail
ret
_fail:
li a0, FAILURE
RVTEST_CODE_END
.data
futex_X: .dword 0
futex_Y: .dword 0
futex_Z: .dword 0
count_master: .dword 0
count_child: .dword 0
count_Z: .dword 0
MT_DATA