@@ -1891,6 +1891,192 @@ TEST(future, test2) {
18911891 EXPECT_EQ (v, PROMISE_VALUE );
18921892}
18931893
1894+ TEST (qrwlock, BasicLockUnlock) {
1895+ qrwlock mtx;
1896+
1897+ // Test exclusive lock
1898+ EXPECT_EQ (0 , mtx.lock (WLOCK ));
1899+ mtx.unlock ();
1900+
1901+ // Test shared lock
1902+ EXPECT_EQ (0 , mtx.lock (RLOCK ));
1903+ mtx.unlock ();
1904+ }
1905+
1906+ TEST (qrwlock, TryLock) {
1907+ qrwlock mtx;
1908+
1909+ // Try lock should succeed when unlocked
1910+ EXPECT_TRUE (mtx.try_lock (WLOCK ) == 0 );
1911+ // Try lock should fail when write-locked
1912+ EXPECT_FALSE (mtx.try_lock (WLOCK ) == 0 );
1913+ EXPECT_FALSE (mtx.try_lock (RLOCK ) == 0 );
1914+ mtx.unlock ();
1915+
1916+ // Try shared lock should succeed when unlocked
1917+ EXPECT_TRUE (mtx.try_lock (RLOCK ) == 0 );
1918+ // Additional shared locks should succeed
1919+ EXPECT_TRUE (mtx.try_lock (RLOCK ) == 0 );
1920+ // Write lock should fail when read-locked
1921+ EXPECT_FALSE (mtx.try_lock (WLOCK ) == 0 );
1922+ mtx.unlock ();
1923+ mtx.unlock ();
1924+ }
1925+
1926+ TEST (qrwlock, MultipleReaders) {
1927+ qrwlock mtx;
1928+ constexpr int NUM_READERS = 100 ;
1929+
1930+ // Acquire many shared locks
1931+ for (int i = 0 ; i < NUM_READERS ; i++) {
1932+ EXPECT_TRUE (mtx.try_lock (RLOCK ) == 0 );
1933+ }
1934+
1935+ // Write lock should fail
1936+ EXPECT_FALSE (mtx.try_lock (WLOCK ) == 0 );
1937+
1938+ // Release all shared locks
1939+ for (int i = 0 ; i < NUM_READERS ; i++) {
1940+ mtx.unlock ();
1941+ }
1942+
1943+ // Now write lock should succeed
1944+ EXPECT_TRUE (mtx.try_lock (WLOCK ) == 0 );
1945+ mtx.unlock ();
1946+ }
1947+
1948+ TEST (qrwlock, ConcurrentReaders) {
1949+ qrwlock mtx;
1950+ std::atomic<int > active_readers{0 };
1951+ std::atomic<int > max_concurrent{0 };
1952+ std::atomic<uint64_t > total_ops{0 };
1953+ constexpr int NUM_THREADS = 32 ;
1954+ constexpr int OPS_PER_THREAD = 1000 ;
1955+
1956+ std::vector<photon::join_handle*> handles;
1957+ for (int i = 0 ; i < NUM_THREADS ; i++) {
1958+ handles.emplace_back (photon::thread_enable_join (
1959+ photon::thread_create11 ([&]() {
1960+ for (int j = 0 ; j < OPS_PER_THREAD ; j++) {
1961+ mtx.lock (RLOCK );
1962+ int cur = active_readers.fetch_add (1 , std::memory_order_relaxed) + 1 ;
1963+ // Track max concurrent readers
1964+ int prev_max = max_concurrent.load (std::memory_order_relaxed);
1965+ while (cur > prev_max &&
1966+ !max_concurrent.compare_exchange_weak (prev_max, cur)) {}
1967+ photon::thread_yield ();
1968+ active_readers.fetch_sub (1 , std::memory_order_relaxed);
1969+ mtx.unlock ();
1970+ total_ops.fetch_add (1 , std::memory_order_relaxed);
1971+ }
1972+ })));
1973+ }
1974+
1975+ for (auto & h : handles) {
1976+ photon::thread_join (h);
1977+ }
1978+
1979+ EXPECT_EQ (0 , active_readers.load ());
1980+ EXPECT_GT (max_concurrent.load (), 1 ); // Should have concurrent readers
1981+ EXPECT_EQ (NUM_THREADS * OPS_PER_THREAD , total_ops.load ());
1982+ LOG_INFO (" Max concurrent readers: `" , max_concurrent.load ());
1983+ }
1984+
1985+ TEST (qrwlock, ReaderWriterExclusion) {
1986+ qrwlock mtx;
1987+ std::atomic<bool > writing{false };
1988+ std::atomic<int > active_readers{0 };
1989+ std::atomic<uint64_t > read_ops{0 };
1990+ std::atomic<uint64_t > write_ops{0 };
1991+ constexpr int NUM_READERS = 16 ;
1992+ constexpr int NUM_WRITERS = 4 ;
1993+ constexpr int OPS_PER_THREAD = 500 ;
1994+
1995+ std::vector<photon::join_handle*> handles;
1996+
1997+ // Reader threads
1998+ for (int i = 0 ; i < NUM_READERS ; i++) {
1999+ handles.emplace_back (photon::thread_enable_join (
2000+ photon::thread_create11 ([&]() {
2001+ for (int j = 0 ; j < OPS_PER_THREAD ; j++) {
2002+ mtx.lock (RLOCK );
2003+ active_readers.fetch_add (1 , std::memory_order_relaxed);
2004+ EXPECT_FALSE (writing.load (std::memory_order_acquire));
2005+ photon::thread_yield ();
2006+ EXPECT_FALSE (writing.load (std::memory_order_acquire));
2007+ active_readers.fetch_sub (1 , std::memory_order_relaxed);
2008+ mtx.unlock ();
2009+ read_ops.fetch_add (1 , std::memory_order_relaxed);
2010+ }
2011+ })));
2012+ }
2013+
2014+ // Writer threads
2015+ for (int i = 0 ; i < NUM_WRITERS ; i++) {
2016+ handles.emplace_back (photon::thread_enable_join (
2017+ photon::thread_create11 ([&]() {
2018+ for (int j = 0 ; j < OPS_PER_THREAD ; j++) {
2019+ mtx.lock (WLOCK );
2020+ EXPECT_EQ (0 , active_readers.load (std::memory_order_acquire));
2021+ writing.store (true , std::memory_order_release);
2022+ photon::thread_yield ();
2023+ EXPECT_EQ (0 , active_readers.load (std::memory_order_acquire));
2024+ writing.store (false , std::memory_order_release);
2025+ mtx.unlock ();
2026+ write_ops.fetch_add (1 , std::memory_order_relaxed);
2027+ }
2028+ })));
2029+ }
2030+
2031+ for (auto & h : handles) {
2032+ photon::thread_join (h);
2033+ }
2034+
2035+ EXPECT_EQ (NUM_READERS * OPS_PER_THREAD , read_ops.load ());
2036+ EXPECT_EQ (NUM_WRITERS * OPS_PER_THREAD , write_ops.load ());
2037+ LOG_INFO (" Read ops: `, Write ops: `" , read_ops.load (), write_ops.load ());
2038+ }
2039+
2040+ TEST (qrwlock, LockTimeout) {
2041+ qrwlock mtx;
2042+
2043+ // Acquire write lock in main coroutine
2044+ EXPECT_EQ (0 , mtx.lock (WLOCK ));
2045+
2046+ // Try to acquire read lock with timeout in another coroutine
2047+ auto jh = photon::thread_enable_join (
2048+ photon::thread_create11 ([&]() {
2049+ EXPECT_EQ (-1 , mtx.lock (RLOCK , 100 * 1000 ));
2050+ EXPECT_EQ (ETIMEDOUT , errno);
2051+ }));
2052+
2053+ // Must yield to let the waiter run and timeout
2054+ photon::thread_usleep (200 * 1000 ); // Sleep longer than timeout
2055+
2056+ photon::thread_join (jh);
2057+ mtx.unlock ();
2058+ }
2059+
2060+ TEST (qrwlock, WriteLockTimeout) {
2061+ qrwlock mtx;
2062+
2063+ // Acquire read lock in main coroutine
2064+ EXPECT_EQ (0 , mtx.lock (RLOCK ));
2065+
2066+ // Try to acquire write lock with timeout in another coroutine
2067+ auto jh = photon::thread_enable_join (
2068+ photon::thread_create11 ([&]() {
2069+ EXPECT_EQ (-1 , mtx.lock (WLOCK , 100 * 1000 ));
2070+ EXPECT_EQ (ETIMEDOUT , errno);
2071+ }));
2072+
2073+ // Must yield to let the waiter run and timeout
2074+ photon::thread_usleep (200 * 1000 ); // Sleep longer than timeout
2075+
2076+ photon::thread_join (jh);
2077+ mtx.unlock ();
2078+ }
2079+
18942080int main (int argc, char ** arg)
18952081{
18962082 if (!photon::is_using_default_engine ()) return 0 ;
0 commit comments