1 /* Copyright (c) 2002-2023. The SimGrid Team. All rights reserved. */
3 /* This program is free software; you can redistribute it and/or modify it
4 * under the terms of the license (GNU LGPL) which comes with this package. */
6 /* Object access checker, for use in sthread. */
8 #include "simgrid/modelchecker.h"
9 #include "simgrid/s4u/Actor.hpp"
10 #include "simgrid/simix.hpp"
11 #include "src/kernel/actor/ActorImpl.hpp"
12 #include "src/kernel/actor/SimcallObserver.hpp"
13 #include "src/sthread/sthread.h"
14 #include "xbt/string.hpp"
16 #include <unordered_map>
18 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(sthread);
20 namespace simgrid::sthread {
21 XBT_DECLARE_ENUM_CLASS(AccessType, ENTER, EXIT, BOTH);
23 class ObjectAccessObserver final : public simgrid::kernel::actor::SimcallObserver {
31 ObjectAccessObserver(simgrid::kernel::actor::ActorImpl* actor, AccessType type, void* objaddr, const char* objname,
32 const char* file, int line)
33 : SimcallObserver(actor), type_(type), objaddr_(objaddr), objname_(objname), file_(file), line_(line)
36 void serialize(std::stringstream& stream) const override;
37 std::string to_string() const override;
39 void ObjectAccessObserver::serialize(std::stringstream& stream) const
41 stream << (short)mc::Transition::Type::OBJECT_ACCESS << ' ';
42 stream << (short)type_ << ' ' << objaddr_ << ' ' << objname_ << ' ' << file_ << ' ' << line_;
44 std::string ObjectAccessObserver::to_string() const
46 return std::string("AccessObject(") + objname_ + ")";
48 }; // namespace simgrid::sthread
51 simgrid::kernel::actor::ActorImpl* owner = nullptr;
52 const char* file = nullptr;
54 int recursive_depth = 0;
55 explicit ObjectOwner(simgrid::kernel::actor::ActorImpl* o) : owner(o) {}
58 std::unordered_map<void*, ObjectOwner*> owners;
59 static void clean_owners()
61 for (auto it = owners.begin(); it != owners.end();) {
63 it = owners.erase(it);
66 static ObjectOwner* get_owner(void* object)
69 std::atexit(clean_owners);
70 if (auto it = owners.find(object); it != owners.end())
72 auto* o = new ObjectOwner(nullptr);
73 owners.emplace(object, o);
77 int sthread_access_begin(void* objaddr, const char* objname, const char* file, int line, const char* func)
80 auto* self = simgrid::kernel::actor::ActorImpl::self();
81 simgrid::sthread::ObjectAccessObserver observer(self, simgrid::sthread::AccessType::ENTER, objaddr, objname, file,
83 bool success = simgrid::kernel::actor::simcall_answered(
84 [self, objaddr, objname, file, line]() -> bool {
85 XBT_INFO("%s takes %s", self->get_cname(), objname);
86 auto* ownership = get_owner(objaddr);
87 if (ownership->owner == self) {
88 ownership->recursive_depth++;
90 } else if (ownership->owner != nullptr) {
91 auto msg = std::string("Unprotected concurent access to ") + objname + ": " + ownership->owner->get_name();
92 if (not xbt_log_no_loc) {
93 msg += simgrid::xbt::string_printf(" at %s:%d", ownership->file, ownership->line);
94 if (ownership->recursive_depth > 1) {
95 msg += simgrid::xbt::string_printf(" (and %d other locations)", ownership->recursive_depth - 1);
96 if (ownership->recursive_depth != 2)
100 msg += simgrid::xbt::string_printf(" from %d location", ownership->recursive_depth);
101 if (ownership->recursive_depth != 1)
104 msg += " vs " + self->get_name();
106 msg += std::string(" (locations hidden because of --log=no_loc).");
108 msg += simgrid::xbt::string_printf(" at %s:%d.", file, line);
109 XBT_CRITICAL("%s", msg.c_str());
112 ownership->owner = self;
113 ownership->file = file;
114 ownership->line = line;
115 ownership->recursive_depth = 1;
120 xbt_assert(success, "Problem detected, bailing out");
124 void sthread_access_end(void* objaddr, const char* objname, const char* file, int line, const char* func)
127 auto* self = simgrid::kernel::actor::ActorImpl::self();
128 simgrid::sthread::ObjectAccessObserver observer(self, simgrid::sthread::AccessType::EXIT, objaddr, objname, file,
130 simgrid::kernel::actor::simcall_answered(
131 [self, objaddr, objname]() -> void {
132 XBT_INFO("%s releases %s", self->get_cname(), objname);
133 auto* ownership = get_owner(objaddr);
134 xbt_assert(ownership->owner == self,
135 "safety check failed: %s is not owner of the object it's releasing. That object owned by %s.",
136 self->get_cname(), (ownership->owner == nullptr ? "nobody" : ownership->owner->get_cname()));
137 ownership->recursive_depth--;
138 if (ownership->recursive_depth == 0)
139 ownership->owner = nullptr;