diff --git a/interfaces/kits/js/BUILD.gn b/interfaces/kits/js/BUILD.gn index 3c4ddabd989b02f0a78d67f916b3c4cf72541662..233e75e4d4448805d526b1cce9c7594543dbf8c3 100644 --- a/interfaces/kits/js/BUILD.gn +++ b/interfaces/kits/js/BUILD.gn @@ -716,6 +716,7 @@ ohos_shared_library("ani_file_fs") { "src/mod_fs/class_watcher/ani/watch_event_wrapper.cpp", "src/mod_fs/class_watcher/fs_file_watcher.cpp", "src/mod_fs/class_watcher/fs_watcher.cpp", + "src/mod_fs/class_watcher/watcher_data_cache.cpp", "src/mod_fs/fs_utils.cpp", "src/mod_fs/properties/access_core.cpp", "src/mod_fs/properties/ani/access_ani.cpp", diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp index 24e09e958d69f8ec7383e2a6ffe350e53a79b5d0..b41b17385269433378235b7631930594e4c03987 100644 --- a/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.cpp @@ -28,12 +28,6 @@ namespace OHOS::FileManagement::ModuleFileIO { using namespace std; -mutex FsFileWatcher::watchMutex_; - -FsFileWatcher::FsFileWatcher() {} - -FsFileWatcher::~FsFileWatcher() {} - int32_t FsFileWatcher::GetNotifyId() { return notifyFd_; @@ -54,51 +48,38 @@ bool FsFileWatcher::InitNotify() return true; } -tuple FsFileWatcher::CheckEventWatched(const string &fileName, const uint32_t &event) -{ - int32_t wd = -1; - auto iter = wdFileNameMap_.find(fileName); - if (iter == wdFileNameMap_.end()) { - return { false, wd }; - } - wd = iter->second.first; - if ((iter->second.second & event) == event) { - return { true, wd }; - } - return { false, wd }; -} - int32_t FsFileWatcher::StartNotify(shared_ptr info) { - lock_guard lock(watchMutex_); if (notifyFd_ < 0) { HILOGE("Failed to start notify notifyFd_:%{public}d", notifyFd_); return EIO; } - auto [isWatched, wd] = CheckEventWatched(info->fileName, info->events); + auto [isWatched, wd] = dataCache_.FindWatchedWd(info->fileName, info->events); if (isWatched && wd > 0) { info->wd = wd; return ERRNO_NOERR; } + uint32_t watchEvents = 0; if (wd != -1) { - watchEvents = wdFileNameMap_[info->fileName].second | info->events; + watchEvents = dataCache_.GetFileEvents(info->fileName) | info->events; } else { watchEvents = info->events; } + int32_t newWd = inotify_add_watch(notifyFd_, info->fileName.c_str(), watchEvents); if (newWd < 0) { HILOGE("Failed to start notify errCode:%{public}d", errno); return errno; } + info->wd = newWd; - wdFileNameMap_[info->fileName].first = newWd; - wdFileNameMap_[info->fileName].second = watchEvents; + dataCache_.UpdateWatchedEvents(info->fileName, newWd, watchEvents); return ERRNO_NOERR; } -int32_t FsFileWatcher::NotifyToWatchNewEvents(const string &fileName, const int32_t &wd, const uint32_t &watchEvents) +int32_t FsFileWatcher::NotifyToWatchNewEvents(const string &fileName, int32_t wd, uint32_t watchEvents) { int32_t newWd = inotify_add_watch(notifyFd_, fileName.c_str(), watchEvents); if (newWd < 0) { @@ -110,7 +91,8 @@ int32_t FsFileWatcher::NotifyToWatchNewEvents(const string &fileName, const int3 HILOGE("New notify wd is error"); return EIO; } - wdFileNameMap_[fileName].second = watchEvents; + + dataCache_.UpdateWatchedEvents(fileName, wd, watchEvents); return ERRNO_NOERR; } @@ -118,19 +100,22 @@ int32_t FsFileWatcher::CloseNotifyFd() { int32_t closeRet = ERRNO_NOERR; - if (watcherInfoSet_.size() == 0) { + if (!dataCache_.HasWatcherInfo()) { run_ = false; closeRet = close(notifyFd_); if (closeRet != 0) { HILOGE("Failed to stop notify close fd errCode:%{public}d", errno); } notifyFd_ = -1; + closeRet = close(eventFd_); if (closeRet != 0) { HILOGE("Failed to close eventfd errCode:%{public}d", errno); } eventFd_ = -1; + HILOGI("Note: DestroyTaskThead begin..."); DestroyTaskThead(); + HILOGI("Note: DestroyTaskThead done!"); } closed_ = false; @@ -152,20 +137,21 @@ int FsFileWatcher::CloseNotifyFdLocked() int32_t FsFileWatcher::StopNotify(shared_ptr info) { - unique_lock lock(watchMutex_); if (notifyFd_ < 0) { HILOGE("Failed to stop notify notifyFd_:%{public}d", notifyFd_); return EIO; } - uint32_t newEvents = RemoveWatcherInfo(info); - if (newEvents > 0) { + + uint32_t remainingEvents = RemoveWatcherInfo(info); + if (remainingEvents > 0) { if (access(info->fileName.c_str(), F_OK) == 0) { - return NotifyToWatchNewEvents(info->fileName, info->wd, newEvents); + return NotifyToWatchNewEvents(info->fileName, info->wd, remainingEvents); } HILOGE("The Watched file does not exist, and the remaining monitored events will be invalid."); return ERRNO_NOERR; } - int oldWd = -1; + + int32_t oldWd = -1; { lock_guard lock(readMutex_); if (!(closed_ && reading_)) { @@ -174,17 +160,16 @@ int32_t FsFileWatcher::StopNotify(shared_ptr info) HILOGE("rm watch fail"); } } + + int32_t rmRet = 0; if (oldWd == -1) { - int rmErr = errno; - if (access(info->fileName.c_str(), F_OK) == 0) { - HILOGE("Failed to stop notify errCode:%{public}d", rmErr); - wdFileNameMap_.erase(info->fileName); - CloseNotifyFdLocked(); - return rmErr; - } + rmRet = errno; + HILOGE("Failed to stop notify errCode:%{public}d", rmRet); } - wdFileNameMap_.erase(info->fileName); - return CloseNotifyFdLocked(); + + dataCache_.RemoveFileWatcher(info->fileName); + int32_t closeRet = CloseNotifyFdLocked(); + return rmRet != 0 ? rmRet : closeRet; } void FsFileWatcher::ReadNotifyEvent() @@ -193,6 +178,7 @@ void FsFileWatcher::ReadNotifyEvent() int32_t index = 0; char buf[BUF_SIZE] = { 0 }; struct inotify_event *event = nullptr; + do { len = read(notifyFd_, &buf, sizeof(buf)); if (len < 0 && errno != EINTR) { @@ -200,6 +186,7 @@ void FsFileWatcher::ReadNotifyEvent() break; } } while (len < 0); + while (index < len) { event = reinterpret_cast(buf + index); NotifyEvent(event); @@ -217,7 +204,9 @@ void FsFileWatcher::ReadNotifyEventLocked() } reading_ = true; } + ReadNotifyEvent(); + { lock_guard lock(readMutex_); reading_ = false; @@ -243,95 +232,72 @@ void FsFileWatcher::GetNotifyEvent() if (run_) { return; } + run_ = true; nfds_t nfds = 2; - struct pollfd fds[2]; + struct pollfd fds[2] = { { 0 } }; fds[0].fd = eventFd_; - fds[0].events = 0; + fds[0].events = POLLIN; fds[1].fd = notifyFd_; fds[1].events = POLLIN; int32_t ret = 0; + while (run_) { - ret = poll(fds, nfds, -1); + ret = poll(fds, nfds, pollTimeoutMs); if (ret > 0) { if (static_cast(fds[0].revents) & POLLNVAL) { run_ = false; - return; + break; + } + if (static_cast(fds[0].revents) & POLLIN) { + run_ = false; + break; } if (static_cast(fds[1].revents) & POLLIN) { ReadNotifyEventLocked(); } - } else if (ret < 0 && errno == EINTR) { continue; - } else { + } + if (ret == 0) { + continue; + } + if (ret < 0 && errno == EINTR) { + continue; + } + if (ret < 0 && errno != EINTR) { HILOGE("Failed to poll NotifyFd, errno=%{public}d", errno); - return; + break; } } } -bool FsFileWatcher::AddWatcherInfo(const string &fileName, shared_ptr info) +bool FsFileWatcher::AddWatcherInfo(shared_ptr info) { - for (auto &iter : watcherInfoSet_) { - if (iter->fileName == info->fileName && iter->events == info->events) { - bool isSame = iter->callback->IsStrictEquals(info->callback); - if (isSame) { - HILOGE("Faile to add watcher, fileName:%{public}s the callback is same", fileName.c_str()); - return false; - } - } - } - watcherInfoSet_.insert(info); - return true; + return dataCache_.AddWatcherInfo(info); } uint32_t FsFileWatcher::RemoveWatcherInfo(shared_ptr info) { - watcherInfoSet_.erase(info); - uint32_t otherEvents = 0; - for (const auto &iter : watcherInfoSet_) { - if (iter->fileName == info->fileName && iter->wd > 0) { - otherEvents |= iter->events; - } - } - return otherEvents; -} - -static bool CheckIncludeEvent(const uint32_t &mask, const uint32_t &event) -{ - if ((mask & event) > 0) { - return true; - } - return false; + return dataCache_.RemoveWatcherInfo(info); } void FsFileWatcher::NotifyEvent(const struct inotify_event *event) { - lock_guard lock(watchMutex_); - string tempFileName; - auto found = find_if(wdFileNameMap_.begin(), wdFileNameMap_.end(), - [event](const pair> &iter) { return iter.second.first == event->wd; }); - if (found != wdFileNameMap_.end()) { - tempFileName = found->first; + auto [matched, fileName, watcherInfos] = dataCache_.FindWatcherInfos(event->wd, event->mask); + if (!matched) { + HILOGE("Cannot find matched watcherInfos"); + return; } - for (const auto &iter : watcherInfoSet_) { - string fileName = tempFileName; - uint32_t watchEvent = 0; - if ((iter->fileName == fileName) && (iter->wd > 0)) { - watchEvent = iter->events; - } - if (!CheckIncludeEvent(event->mask, watchEvent)) { - continue; - } + for (const auto &info : watcherInfos) { if (event->len > 0) { fileName += "/" + string(event->name); } - iter->TriggerCallback(fileName, event->mask & IN_ALL_EVENTS, event->cookie); + info->TriggerCallback(fileName, event->mask & IN_ALL_EVENTS, event->cookie); } } -bool FsFileWatcher::CheckEventValid(const uint32_t &event) +bool FsFileWatcher::CheckEventValid(uint32_t event) { if ((event & IN_ALL_EVENTS) == event) { return true; @@ -344,12 +310,20 @@ bool FsFileWatcher::CheckEventValid(const uint32_t &event) void FsFileWatcher::DestroyTaskThead() { if (taskThead_.joinable()) { - taskThead_.join(); + if (taskThead_.get_id() != std::this_thread::get_id()) { + taskThead_.join(); + } else { + taskThead_.detach(); + } } - lock_guard lock(taskMutex_); - if (taskRunning_) { - taskRunning_ = false; + { + lock_guard lock(taskMutex_); + if (taskRunning_) { + taskRunning_ = false; + run_ = false; + } } + dataCache_.ClearCache(); } } // namespace OHOS::FileManagement::ModuleFileIO diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h index 17be3a40a699c0077df52cc16e08aa927bb44145..9af0f9f382dd2d1c33fef15c4e3d0bf7cefd8b04 100644 --- a/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h +++ b/interfaces/kits/js/src/mod_fs/class_watcher/fs_file_watcher.h @@ -18,14 +18,13 @@ #include #include #include -#include -#include #include #include "filemgmt_libfs.h" #include "fs_watch_entity.h" #include "singleton.h" +#include "watcher_data_cache.h" namespace OHOS::FileManagement::ModuleFileIO { using namespace std; @@ -34,30 +33,33 @@ constexpr int BUF_SIZE = 1024; class FsFileWatcher : public Singleton { public: - FsFileWatcher(); - ~FsFileWatcher(); int32_t GetNotifyId(); bool InitNotify(); int StartNotify(shared_ptr info); int StopNotify(shared_ptr info); void GetNotifyEvent(); void AsyncGetNotifyEvent(); - bool AddWatcherInfo(const string &fileName, shared_ptr info); - bool CheckEventValid(const uint32_t &event); + bool AddWatcherInfo(shared_ptr info); + bool CheckEventValid(uint32_t event); + +public: + FsFileWatcher() = default; + ~FsFileWatcher() = default; + FsFileWatcher(const FsFileWatcher &) = delete; + FsFileWatcher &operator=(const FsFileWatcher &) = delete; private: uint32_t RemoveWatcherInfo(shared_ptr info); - tuple CheckEventWatched(const string &fileName, const uint32_t &event); void NotifyEvent(const struct inotify_event *event); int CloseNotifyFd(); int CloseNotifyFdLocked(); - int NotifyToWatchNewEvents(const string &fileName, const int &wd, const uint32_t &watchEvents); + int NotifyToWatchNewEvents(const string &fileName, int wd, uint32_t watchEvents); void ReadNotifyEvent(); void ReadNotifyEventLocked(); void DestroyTaskThead(); private: - static mutex watchMutex_; + static constexpr int32_t pollTimeoutMs = 500; mutex taskMutex_; mutex readMutex_; @@ -70,11 +72,7 @@ private: bool closed_ = false; int32_t notifyFd_ = -1; int32_t eventFd_ = -1; - unordered_set> watcherInfoSet_; - unordered_map> wdFileNameMap_; - - FsFileWatcher(const FsFileWatcher &) = delete; - FsFileWatcher &operator=(const FsFileWatcher &) = delete; + WatcherDataCache dataCache_; }; } // namespace OHOS::FileManagement::ModuleFileIO diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_data_cache.cpp b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_data_cache.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83b5e2a51d734de7f54b020ef54e0bbffa608ae0 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_data_cache.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2025 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. + */ +#include "watcher_data_cache.h" +#include "filemgmt_libhilog.h" + +namespace OHOS::FileManagement::ModuleFileIO { + +bool WatcherDataCache::AddWatcherInfo(std::shared_ptr info) +{ + std::lock_guard lock(cacheMutex_); + for (auto &iter : watcherInfoCache_) { + if (iter->fileName == info->fileName && iter->events == info->events) { + bool isSame = iter->callback->IsStrictEquals(info->callback); + if (isSame) { + HILOGE("Failed to add watcher, fileName:%{private}s the callback is same", info->fileName.c_str()); + return false; + } + } + } + watcherInfoCache_.push_back(info); + wdFileNameCache_[info->fileName] = std::make_pair(info->wd, info->events); + return true; +} + +uint32_t WatcherDataCache::RemoveWatcherInfo(std::shared_ptr info) +{ + std::lock_guard lock(cacheMutex_); + auto it = std::find(watcherInfoCache_.begin(), watcherInfoCache_.end(), info); + if (it != watcherInfoCache_.end()) { + watcherInfoCache_.erase(it); + } + + uint32_t remainingEvents = 0; + for (const auto &iter : watcherInfoCache_) { + if (iter->fileName == info->fileName && iter->wd > 0) { + remainingEvents |= iter->events; + } + } + + return remainingEvents; +} + +bool WatcherDataCache::RemoveFileWatcher(const std::string &fileName) +{ + std::lock_guard lock(cacheMutex_); + + auto iter = wdFileNameCache_.find(fileName); + if (iter == wdFileNameCache_.end()) { + return false; + } + wdFileNameCache_.erase(iter); + + watcherInfoCache_.erase(std::remove_if(watcherInfoCache_.begin(), watcherInfoCache_.end(), + [&fileName](const std::shared_ptr &info) { + return info->fileName == fileName; + }), watcherInfoCache_.end()); + + return true; +} + +std::tuple WatcherDataCache::FindWatchedWd(const std::string &fileName, uint32_t event) +{ + std::lock_guard lock(cacheMutex_); + + int32_t wd = -1; + auto iter = wdFileNameCache_.find(fileName); + if (iter == wdFileNameCache_.end()) { + return { false, wd }; + } + + wd = iter->second.first; + if ((iter->second.second & event) == event) { + return { true, wd }; + } + + return { false, wd }; +} + +bool WatcherDataCache::UpdateWatchedEvents(const std::string &fileName, int32_t wd, uint32_t events) +{ + std::lock_guard lock(cacheMutex_); + auto iter = wdFileNameCache_.find(fileName); + if (iter == wdFileNameCache_.end()) { + return false; + } + + iter->second = std::make_pair(wd, events); + return true; +} + +static bool CheckIncludeEvent(uint32_t mask, uint32_t event) +{ + return (mask & event) > 0; +} + +std::tuple>> WatcherDataCache::FindWatcherInfos( + int32_t wd, uint32_t eventMask) +{ + std::lock_guard lock(cacheMutex_); + std::string fileName; + bool found = false; + for (const auto &[key, val] : wdFileNameCache_) { + if (val.first == wd) { + fileName = key; + found = true; + break; + } + } + + if (!found) { + return { false, "", {} }; + } + + std::vector> matchedInfos; + for (const auto &info : watcherInfoCache_) { + uint32_t watchEvent = 0; + if ((info->fileName == fileName) && (info->wd > 0)) { + watchEvent = info->events; + } + if (CheckIncludeEvent(eventMask, watchEvent)) { + matchedInfos.push_back(info); + } + } + return { !matchedInfos.empty(), fileName, matchedInfos }; +} + +uint32_t WatcherDataCache::GetFileEvents(const std::string &fileName) +{ + std::lock_guard lock(cacheMutex_); + auto iter = wdFileNameCache_.find(fileName); + if (iter == wdFileNameCache_.end()) { + return 0; + } + return iter->second.second; +} + +bool WatcherDataCache::HasWatcherInfo() const +{ + std::lock_guard lock(cacheMutex_); + return !watcherInfoCache_.empty(); +} + +void WatcherDataCache::ClearCache() +{ + std::lock_guard lock(cacheMutex_); + watcherInfoCache_.clear(); + wdFileNameCache_.clear(); +} + +} // namespace OHOS::FileManagement::ModuleFileIO \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/class_watcher/watcher_data_cache.h b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_data_cache.h new file mode 100644 index 0000000000000000000000000000000000000000..e18d584f1ce7dc0c7814157e638e240724599d81 --- /dev/null +++ b/interfaces/kits/js/src/mod_fs/class_watcher/watcher_data_cache.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 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 INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_WATCHER_DATA_CACHE_H +#define INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_WATCHER_DATA_CACHE_H + +#include +#include +#include +#include +#include + +#include "fs_watch_entity.h" + +namespace OHOS::FileManagement::ModuleFileIO { + +class WatcherDataCache { +public: + bool AddWatcherInfo(std::shared_ptr info); + uint32_t RemoveWatcherInfo(std::shared_ptr info); + bool RemoveFileWatcher(const std::string &fileName); + std::tuple FindWatchedWd(const std::string &fileName, uint32_t event); + bool UpdateWatchedEvents(const std::string &fileName, int32_t wd, uint32_t events); + std::tuple>> FindWatcherInfos( + int32_t wd, uint32_t eventMask); + uint32_t GetFileEvents(const std::string &fileName); + bool HasWatcherInfo() const; + void ClearCache(); + +public: + WatcherDataCache() = default; + ~WatcherDataCache() = default; + WatcherDataCache(const WatcherDataCache &) = delete; + WatcherDataCache &operator=(const WatcherDataCache &) = delete; + +private: + mutable std::mutex cacheMutex_; + std::vector> watcherInfoCache_; + std::unordered_map> wdFileNameCache_; +}; + +} // namespace OHOS::FileManagement::ModuleFileIO +#endif // INTERFACES_KITS_JS_SRC_MOD_FS_CLASS_WATCHER_WATCHER_DATA_CACHE_H \ No newline at end of file diff --git a/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp b/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp index 382f494ec67385ce04a25f7bd2d66e0be80403c8..1ea4a713a55989c1340331c6c47ef623c5a57711 100644 --- a/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp +++ b/interfaces/kits/js/src/mod_fs/properties/watcher_core.cpp @@ -98,7 +98,7 @@ FsResult WatcherCore::DoCreateWatcher( watchEntity->data_ = info; - bool ret = FsFileWatcher::GetInstance().AddWatcherInfo(info->fileName, info); + bool ret = FsFileWatcher::GetInstance().AddWatcherInfo(info); if (!ret) { HILOGE("Failed to add watcher info."); return FsResult::Error(EINVAL);