/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef THREAD_CONTEXT_H
#define THREAD_CONTEXT_H

#include <atomic>
#include <cstdint>
#include <condition_variable>
#include <csignal>
#include <mutex>
#include <nocopyable.h>
#include <string>

#include "dfx_define.h"

namespace OHOS {
namespace HiviewDFX {
enum ThreadContextStatus : int32_t {
    CONTEXT_UNUSED = -1,
    CONTEXT_READY = -2,
};

struct ThreadContext {
    std::atomic<int32_t> tid {ThreadContextStatus::CONTEXT_UNUSED};
    // for protecting ctx, shared between threads
    std::mutex mtx;
    // the thread should be suspended while unwinding
    // blocked in the signal handler of target thread
    std::condition_variable cv;
    // store unwind context
    ucontext_t* ctx {nullptr};
    // stack range
    uintptr_t stackBottom;
    uintptr_t stackTop;
#if defined(__aarch64__)
    // unwind in signal handler by fp
    uintptr_t pcs[DEFAULT_MAX_LOCAL_FRAME_NUM] {0};
#endif
    std::atomic<size_t> frameSz {0};
    // first stack pointer
    uintptr_t firstFrameSp;

    ~ThreadContext()
    {
        std::unique_lock<std::mutex> lock(mtx);
        if (ctx != nullptr) {
            delete ctx;
            ctx = nullptr;
        }
    };
};

class LocalThreadContext {
public:
    static LocalThreadContext& GetInstance();

    bool GetStackRange(int32_t tid, uintptr_t& stackBottom, uintptr_t& stackTop);
    std::shared_ptr<ThreadContext> CollectThreadContext(int32_t tid);
    std::shared_ptr<ThreadContext> GetThreadContext(int32_t tid);
    void ReleaseThread(int32_t tid);
    void CleanUp();

private:
    LocalThreadContext() = default;
    DISALLOW_COPY_AND_MOVE(LocalThreadContext);

    static void CopyContextAndWaitTimeout(int sig, siginfo_t *si, void *context);
    bool SignalRequestThread(int32_t tid, ThreadContext* ctx);
    void InitSignalHandler();

private:
    std::mutex localMutex_;
};
} // namespace Dfx
} // namespace OHOS
#endif