1 /* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef SYNC_SEMAPHORE_H 17 #define SYNC_SEMAPHORE_H 18 19 #include <atomic> 20 #include <chrono> 21 #include <thread> 22 #include <mutex> 23 #include <condition_variable> 24 #include "sched/execute_ctx.h" 25 #include "delayed_worker.h" 26 #ifndef _MSC_VER 27 #include <unistd.h> 28 #include <sys/syscall.h> 29 #include <linux/futex.h> 30 #endif 31 32 namespace ffrt { 33 #ifndef _MSC_VER 34 class semaphore { 35 uint64_t data; 36 37 public: semaphore()38 semaphore() : data(0) 39 { 40 } semaphore(uint32_t d)41 semaphore(uint32_t d) : data(d) 42 { 43 } 44 semaphore(semaphore const&) = delete; 45 void operator=(semaphore const&) = delete; 46 acquire()47 void acquire() 48 { 49 const uint64_t w = static_cast<uint64_t>(1) << 32; 50 51 uint64_t d = __atomic_fetch_add(&data, w, __ATOMIC_RELAXED); 52 for (;;) { 53 // RunnableTaskNumber == 0 ? 54 if (static_cast<uint32_t>(d) == 0) { 55 // RQ is empty, then sleep 56 syscall(SYS_futex, &data, FUTEX_WAIT_PRIVATE, 0, nullptr, nullptr, 0); 57 d = __atomic_load_n(&data, __ATOMIC_RELAXED); 58 } else if (__atomic_compare_exchange_n(&data, &d, d - 1 - w, 1, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED)) { 59 break; 60 } 61 } 62 } 63 release()64 void release() 65 { 66 // increase RunnableTaskNumber 67 uint64_t d = __atomic_fetch_add(&data, 1, __ATOMIC_RELEASE); 68 // overflow 69 if (static_cast<uint32_t>(d) == ~0u) { 70 abort(); 71 } 72 // if RunnableTaskNumber == 0 && SleepWorkerNumber > 0, then wake up sleep worker 73 // if RQ is empty before new task is enqueued, RunnableTaskNumber is from 0 to 1 74 if (static_cast<uint32_t>(d) == 0 && (d >> 32) > 0) { 75 syscall(SYS_futex, &data, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0); 76 } 77 78 // if RunnableTaskNumber >= 4 && SleepWorkerNumber > 0 , then wake up another sleep worker 79 if (static_cast<uint32_t>(d) >= 4 && (d >> 32) > 0) { 80 syscall(SYS_futex, &data, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0); 81 } 82 } 83 }; 84 #endif 85 } // namespace ffrt 86 #endif 87