Vulkan/Debug: Split EventListener

Into `ServerEventListener` and `ClientEventListener`.

The new debugger implementation wants to listen to breakpoints being set so that traps can be dynamically enabled.

Bug: b/145351270
Change-Id: Iafe64426874da3aa109d4b59b34cb1e36bd06828
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/48690
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Vulkan/CMakeLists.txt b/src/Vulkan/CMakeLists.txt
index 58ca289..5128fe9 100644
--- a/src/Vulkan/CMakeLists.txt
+++ b/src/Vulkan/CMakeLists.txt
@@ -106,6 +106,7 @@
         Debug/Context.cpp
         Debug/Context.hpp
         Debug/Debug.cpp
+        Debug/EventListener.cpp
         Debug/EventListener.hpp
         Debug/File.cpp
         Debug/File.hpp
diff --git a/src/Vulkan/Debug/Context.cpp b/src/Vulkan/Debug/Context.cpp
index eb43b4a..da3d962 100644
--- a/src/Vulkan/Debug/Context.cpp
+++ b/src/Vulkan/Debug/Context.cpp
@@ -28,65 +28,45 @@
 
 namespace {
 
-class Broadcaster : public vk::dbg::EventListener
+////////////////////////////////////////////////////////////////////////////////
+// Broadcaster - template base class for ServerEventBroadcaster and
+// ClientEventBroadcaster
+////////////////////////////////////////////////////////////////////////////////
+template<typename Listener>
+class Broadcaster : public Listener
 {
 public:
-	using Thread = vk::dbg::Thread;
+	void add(Listener *);
+	void remove(Listener *);
 
-	// EventListener
-	void onThreadStarted(Thread::ID) override;
-	void onThreadStepped(Thread::ID) override;
-	void onLineBreakpointHit(Thread::ID) override;
-	void onFunctionBreakpointHit(Thread::ID) override;
-
-	void add(EventListener *);
-	void remove(EventListener *);
-
-private:
+protected:
 	template<typename F>
 	inline void foreach(F &&);
 
 	template<typename F>
 	inline void modify(F &&);
 
-	using ListenerSet = std::unordered_set<EventListener *>;
+	using ListenerSet = std::unordered_set<Listener *>;
 	std::recursive_mutex mutex;
 	std::shared_ptr<ListenerSet> listeners = std::make_shared<ListenerSet>();
 	int listenersInUse = 0;
 };
 
-void Broadcaster::onThreadStarted(Thread::ID id)
-{
-	foreach([&](EventListener *l) { l->onThreadStarted(id); });
-}
-
-void Broadcaster::onThreadStepped(Thread::ID id)
-{
-	foreach([&](EventListener *l) { l->onThreadStepped(id); });
-}
-
-void Broadcaster::onLineBreakpointHit(Thread::ID id)
-{
-	foreach([&](EventListener *l) { l->onLineBreakpointHit(id); });
-}
-
-void Broadcaster::onFunctionBreakpointHit(Thread::ID id)
-{
-	foreach([&](EventListener *l) { l->onFunctionBreakpointHit(id); });
-}
-
-void Broadcaster::add(EventListener *l)
+template<typename Listener>
+void Broadcaster<Listener>::add(Listener *l)
 {
 	modify([&]() { listeners->emplace(l); });
 }
 
-void Broadcaster::remove(EventListener *l)
+template<typename Listener>
+void Broadcaster<Listener>::remove(Listener *l)
 {
 	modify([&]() { listeners->erase(l); });
 }
 
+template<typename Listener>
 template<typename F>
-void Broadcaster::foreach(F &&f)
+void Broadcaster<Listener>::foreach(F &&f)
 {
 	std::unique_lock<std::recursive_mutex> lock(mutex);
 	++listenersInUse;
@@ -95,8 +75,9 @@
 	--listenersInUse;
 }
 
+template<typename Listener>
 template<typename F>
-void Broadcaster::modify(F &&f)
+void Broadcaster<Listener>::modify(F &&f)
 {
 	std::unique_lock<std::recursive_mutex> lock(mutex);
 	if(listenersInUse > 0)
@@ -108,6 +89,57 @@
 	f();
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// ServerEventBroadcaster
+////////////////////////////////////////////////////////////////////////////////
+class ServerEventBroadcaster : public Broadcaster<vk::dbg::ServerEventListener>
+{
+public:
+	using Thread = vk::dbg::Thread;
+
+	void onThreadStarted(Thread::ID id) override
+	{
+		foreach([&](auto *l) { l->onThreadStarted(id); });
+	}
+
+	void onThreadStepped(Thread::ID id) override
+	{
+		foreach([&](auto *l) { l->onThreadStepped(id); });
+	}
+
+	void onLineBreakpointHit(Thread::ID id) override
+	{
+		foreach([&](auto *l) { l->onLineBreakpointHit(id); });
+	}
+
+	void onFunctionBreakpointHit(Thread::ID id) override
+	{
+		foreach([&](auto *l) { l->onFunctionBreakpointHit(id); });
+	}
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// ClientEventBroadcaster
+////////////////////////////////////////////////////////////////////////////////
+class ClientEventBroadcaster : public Broadcaster<vk::dbg::ClientEventListener>
+{
+public:
+	void onSetBreakpoint(const vk::dbg::Location &location, bool &handled) override
+	{
+		foreach([&](auto *l) { l->onSetBreakpoint(location, handled); });
+	}
+
+	void onSetBreakpoint(const std::string &func, bool &handled) override
+	{
+		foreach([&](auto *l) { l->onSetBreakpoint(func, handled); });
+	}
+
+	void onBreakpointsChanged() override
+	{
+		foreach([&](auto *l) { l->onBreakpointsChanged(); });
+	}
+};
+
 }  // namespace
 
 namespace vk {
@@ -121,13 +153,17 @@
 public:
 	// Context compliance
 	Lock lock() override;
-	void addListener(EventListener *) override;
-	void removeListener(EventListener *) override;
-	EventListener *broadcast() override;
+	void addListener(ClientEventListener *) override;
+	void removeListener(ClientEventListener *) override;
+	ClientEventListener *clientEventBroadcast() override;
+	void addListener(ServerEventListener *) override;
+	void removeListener(ServerEventListener *) override;
+	ServerEventListener *serverEventBroadcast() override;
 
 	void addFile(const std::shared_ptr<File> &file);
 
-	Broadcaster broadcaster;
+	ServerEventBroadcaster serverEventBroadcaster;
+	ClientEventBroadcaster clientEventBroadcaster;
 
 	std::mutex mutex;
 	std::unordered_map<std::thread::id, std::shared_ptr<Thread>> threadsByStdId;
@@ -150,19 +186,34 @@
 	return Lock(this);
 }
 
-void Context::Impl::addListener(EventListener *l)
+void Context::Impl::addListener(ClientEventListener *l)
 {
-	broadcaster.add(l);
+	clientEventBroadcaster.add(l);
 }
 
-void Context::Impl::removeListener(EventListener *l)
+void Context::Impl::removeListener(ClientEventListener *l)
 {
-	broadcaster.remove(l);
+	clientEventBroadcaster.remove(l);
 }
 
-EventListener *Context::Impl::broadcast()
+ClientEventListener *Context::Impl::clientEventBroadcast()
 {
-	return &broadcaster;
+	return &clientEventBroadcaster;
+}
+
+void Context::Impl::addListener(ServerEventListener *l)
+{
+	serverEventBroadcaster.add(l);
+}
+
+void Context::Impl::removeListener(ServerEventListener *l)
+{
+	serverEventBroadcaster.remove(l);
+}
+
+ServerEventListener *Context::Impl::serverEventBroadcast()
+{
+	return &serverEventBroadcaster;
 }
 
 void Context::Impl::addFile(const std::shared_ptr<File> &file)
@@ -239,7 +290,7 @@
 	thread->setName(name);
 	ctx->threadsByStdId.emplace(std::this_thread::get_id(), thread);
 
-	ctx->broadcast()->onThreadStarted(id);
+	ctx->serverEventBroadcast()->onThreadStarted(id);
 
 	return thread;
 }
diff --git a/src/Vulkan/Debug/Context.hpp b/src/Vulkan/Debug/Context.hpp
index 92f3086..832c9c4 100644
--- a/src/Vulkan/Debug/Context.hpp
+++ b/src/Vulkan/Debug/Context.hpp
@@ -30,7 +30,8 @@
 class Frame;
 class Scope;
 class VariableContainer;
-class EventListener;
+class ClientEventListener;
+class ServerEventListener;
 
 // Context holds the full state of the debugger, including all current files,
 // threads, frames and variables. It also holds a list of EventListeners that
@@ -145,16 +146,27 @@
 	// access.
 	virtual Lock lock() = 0;
 
-	// addListener() registers an EventListener for event notifications.
-	virtual void addListener(EventListener *) = 0;
+	// addListener() registers an ClientEventListener for event notifications.
+	virtual void addListener(ClientEventListener *) = 0;
 
-	// removeListener() unregisters an EventListener that was previously
+	// removeListener() unregisters an ClientEventListener that was previously
 	// registered by a call to addListener().
-	virtual void removeListener(EventListener *) = 0;
+	virtual void removeListener(ClientEventListener *) = 0;
 
-	// broadcast() returns an EventListener that will broadcast all methods on
-	// to all registered EventListeners.
-	virtual EventListener *broadcast() = 0;
+	// clientEventBroadcast() returns an ClientEventListener that will broadcast
+	// all method calls on to all registered ServerEventListeners.
+	virtual ClientEventListener *clientEventBroadcast() = 0;
+
+	// addListener() registers an ServerEventListener for event notifications.
+	virtual void addListener(ServerEventListener *) = 0;
+
+	// removeListener() unregisters an ServerEventListener that was previously
+	// registered by a call to addListener().
+	virtual void removeListener(ServerEventListener *) = 0;
+
+	// serverEventBroadcast() returns an ServerEventListener that will broadcast
+	// all method calls on to all registered ServerEventListeners.
+	virtual ServerEventListener *serverEventBroadcast() = 0;
 };
 
 }  // namespace dbg
diff --git a/src/Vulkan/Debug/EventListener.cpp b/src/Vulkan/Debug/EventListener.cpp
new file mode 100644
index 0000000..19e3ad8
--- /dev/null
+++ b/src/Vulkan/Debug/EventListener.cpp
@@ -0,0 +1,24 @@
+// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
+//
+// 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 "EventListener.hpp"
+
+namespace vk {
+namespace dbg {
+
+ServerEventListener::~ServerEventListener() = default;
+ClientEventListener::~ClientEventListener() = default;
+
+}  // namespace dbg
+}  // namespace vk
diff --git a/src/Vulkan/Debug/EventListener.hpp b/src/Vulkan/Debug/EventListener.hpp
index 8de0dfd..3ab287c 100644
--- a/src/Vulkan/Debug/EventListener.hpp
+++ b/src/Vulkan/Debug/EventListener.hpp
@@ -15,16 +15,20 @@
 #ifndef VK_DEBUG_EVENT_LISTENER_HPP_
 #define VK_DEBUG_EVENT_LISTENER_HPP_
 
+#include "ID.hpp"
+
 namespace vk {
 namespace dbg {
 
+struct Location;
 class Thread;
 
-// EventListener is an interface that is used to listen for thread events.
-class EventListener
+// ServerEventListener is an interface that is used to listen for events raised
+// by the server (the debugger).
+class ServerEventListener
 {
 public:
-	virtual ~EventListener() = default;
+	virtual ~ServerEventListener();
 
 	// onThreadStarted() is called when a new thread begins execution.
 	virtual void onThreadStarted(ID<Thread>) {}
@@ -42,6 +46,23 @@
 	virtual void onFunctionBreakpointHit(ID<Thread>) {}
 };
 
+// ClientEventListener is an interface that is used to listen for events raised
+// by the client (the IDE).
+class ClientEventListener
+{
+public:
+	virtual ~ClientEventListener();
+
+	// onSetBreakpoint() is called when a breakpoint location is set.
+	virtual void onSetBreakpoint(const Location &, bool &handled) {}
+
+	// onSetBreakpoint() is called when a function breakpoint is set.
+	virtual void onSetBreakpoint(const std::string &func, bool &handled) {}
+
+	// onBreakpointsChange() is called after breakpoints have been changed.
+	virtual void onBreakpointsChanged() {}
+};
+
 }  // namespace dbg
 }  // namespace vk
 
diff --git a/src/Vulkan/Debug/Server.cpp b/src/Vulkan/Debug/Server.cpp
index 6b3b692..b6d4443 100644
--- a/src/Vulkan/Debug/Server.cpp
+++ b/src/Vulkan/Debug/Server.cpp
@@ -43,7 +43,7 @@
 namespace vk {
 namespace dbg {
 
-class Server::Impl : public Server, public EventListener
+class Server::Impl : public Server, public ServerEventListener
 {
 public:
 	Impl(const std::shared_ptr<Context> &ctx, int port);
@@ -100,14 +100,27 @@
 	session->registerHandler(
 	    [this](const dap::SetFunctionBreakpointsRequest &req) {
 		    DAP_LOG("SetFunctionBreakpointsRequest receieved");
-		    auto lock = ctx->lock();
+
 		    dap::SetFunctionBreakpointsResponse response;
-		    for(auto const &bp : req.breakpoints)
+		    for(auto const &reqBP : req.breakpoints)
 		    {
-			    DAP_LOG("Setting breakpoint for function '%s'", bp.name.c_str());
-			    lock.addFunctionBreakpoint(bp.name.c_str());
-			    response.breakpoints.push_back({});
+			    DAP_LOG("Setting breakpoint for function '%s'", reqBP.name.c_str());
+
+			    bool verified = false;
+			    ctx->clientEventBroadcast()->onSetBreakpoint(reqBP.name, verified);
+
+			    dap::Breakpoint resBP{};
+			    resBP.verified = verified;
+			    response.breakpoints.emplace_back(std::move(resBP));
 		    }
+		    {
+			    auto lock = ctx->lock();
+			    for(auto const &reqBP : req.breakpoints)
+			    {
+				    lock.addFunctionBreakpoint(reqBP.name.c_str());
+			    }
+		    }
+		    ctx->clientEventBroadcast()->onBreakpointsChanged();
 		    return response;
 	    });
 
@@ -115,7 +128,6 @@
 	    [this](const dap::SetBreakpointsRequest &req)
 	        -> dap::ResponseOrError<dap::SetBreakpointsResponse> {
 		    DAP_LOG("SetBreakpointsRequest receieved");
-		    bool verified = false;
 
 		    size_t numBreakpoints = 0;
 		    if(req.breakpoints.has_value())
@@ -124,14 +136,27 @@
 			    numBreakpoints = breakpoints.size();
 			    if(auto file = this->file(req.source))
 			    {
+				    dap::SetBreakpointsResponse response;
 				    file->clearBreakpoints();
-				    for(auto const &bp : breakpoints)
+				    for(size_t i = 0; i < numBreakpoints; i++)
 				    {
-					    file->addBreakpoint(bp.line);
+					    auto &reqBP = breakpoints[i];
+					    Location location{ file, reqBP.line };
+					    file->addBreakpoint(reqBP.line);
+
+					    bool verified = false;
+					    ctx->clientEventBroadcast()->onSetBreakpoint(location, verified);
+
+					    dap::Breakpoint respBP;
+					    respBP.verified = verified;
+					    respBP.source = req.source;
+					    response.breakpoints.push_back(respBP);
 				    }
-				    verified = true;
+				    ctx->clientEventBroadcast()->onBreakpointsChanged();
+				    return response;
 			    }
-			    else if(req.source.name.has_value())
+
+			    if(req.source.name.has_value())
 			    {
 				    std::vector<int> lines;
 				    lines.reserve(breakpoints.size());
@@ -144,14 +169,16 @@
 			    }
 		    }
 
+		    // Generic response.
 		    dap::SetBreakpointsResponse response;
 		    for(size_t i = 0; i < numBreakpoints; i++)
 		    {
 			    dap::Breakpoint bp;
-			    bp.verified = verified;
+			    bp.verified = false;
 			    bp.source = req.source;
 			    response.breakpoints.push_back(bp);
 		    }
+		    ctx->clientEventBroadcast()->onBreakpointsChanged();
 		    return response;
 	    });
 
diff --git a/src/Vulkan/Debug/Thread.cpp b/src/Vulkan/Debug/Thread.cpp
index 8228465..9e57bc7 100644
--- a/src/Vulkan/Debug/Thread.cpp
+++ b/src/Vulkan/Debug/Thread.cpp
@@ -23,7 +23,7 @@
 
 Thread::Thread(ID id, Context *ctx)
     : id(id)
-    , broadcast(ctx->broadcast())
+    , broadcast(ctx->serverEventBroadcast())
 {}
 
 void Thread::setName(const std::string &name)
@@ -185,4 +185,4 @@
 }
 
 }  // namespace dbg
-}  // namespace vk
\ No newline at end of file
+}  // namespace vk
diff --git a/src/Vulkan/Debug/Thread.hpp b/src/Vulkan/Debug/Thread.hpp
index 690fb37..184d396 100644
--- a/src/Vulkan/Debug/Thread.hpp
+++ b/src/Vulkan/Debug/Thread.hpp
@@ -32,7 +32,7 @@
 
 class File;
 class VariableContainer;
-class EventListener;
+class ServerEventListener;
 
 // Scope is a container for variables and is used to provide source data for the
 // DAP 'Scope' type:
@@ -181,7 +181,7 @@
 	const ID id;
 
 private:
-	EventListener *const broadcast;
+	ServerEventListener *const broadcast;
 
 	void onLocationUpdate(marl::lock &lock) REQUIRES(mutex);