Skip to content

Commit

Permalink
Shared Caching Eviction Tool (#1124)
Browse files Browse the repository at this point in the history
* basic tool structure
* process stdin into a string
* add json parsing and policy execution
* address comments
* cleanup
  • Loading branch information
V-FEXrt authored Feb 25, 2023
1 parent 89715af commit b1aa8eb
Show file tree
Hide file tree
Showing 10 changed files with 324 additions and 3 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ compile_commands.json
/bin/wake*
/bin/preload-wake
/bin/preload-wake.*
/bin/evict-shared-cache.*
/bin/wakebox
/bin/wakebox.*
/bin/wake-format.*
Expand Down
2 changes: 1 addition & 1 deletion build.wake
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export def install: List String => Result String Error = match _

# Build all wake targets
def targets =
buildWake, buildWakeBox, buildFuseDaemon, buildShim, buildBSP, buildLSP, buildWakeFormat, Nil
buildWake, buildWakeBox, buildFuseDaemon, buildShim, buildBSP, buildLSP, buildWakeFormat, buildEvictSharedCache, Nil

def all variant =
require Pass x =
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/job.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@
#include "compat/sigwinch.h"
#include "compat/spawn.h"
#include "database.h"
#include "poll.h"
#include "prim.h"
#include "status.h"
#include "types/data.h"
#include "types/type.h"
#include "util/execpath.h"
#include "util/location.h"
#include "util/poll.h"
#include "util/shell.h"
#include "value.h"

Expand Down
3 changes: 2 additions & 1 deletion src/runtime/poll.cpp → src/util/poll.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <unistd.h>

#include <algorithm>
#include <cstring>

#if defined(__linux__)
#define USE_EPOLL 1
Expand All @@ -44,7 +45,7 @@ struct Poll::detail {
int pfd;
};

Poll::Poll() : imp(new Poll::detail) {
Poll::Poll() : imp(std::make_unique<Poll::detail>()) {
imp->pfd = epoll_create1(EPOLL_CLOEXEC);
if (imp->pfd == -1) {
perror("epoll_create1");
Expand Down
File renamed without changes.
73 changes: 73 additions & 0 deletions tools/evict-shared-cache/command.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2022 SiFive, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of LICENSE.Apache2 along with
* this software. If not, you may obtain a copy at
*
* https://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.
*/

#pragma once

// Open Group Base Specifications Issue 7
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L

#include <iostream>
#include <sstream>
#include <string>

#include "json/json5.h"

enum class CommandType { Read, Write };

struct Command {
CommandType type;
int job_id;

static bool parse(const std::string& str, Command& out) {
JAST json;
std::stringstream parse_errors;
if (!JAST::parse(str, parse_errors, json)) {
std::cerr << "Failed to parse json command: " << parse_errors.str() << std::endl;
return false;
}

const JAST& command = json.get("command");
if (command.kind != JSON_STR) {
std::cerr << "Expected string for 'command' key" << std::endl;
return false;
}

const std::string& command_str = command.value;

CommandType type;
if (command_str == "read") {
type = CommandType::Read;
} else if (command_str == "write") {
type = CommandType::Write;
} else {
std::cerr << "Invalid value for 'command' key. Expected: 'read' | 'write', saw "
<< command_str << std::endl;
return false;
}

const JAST& job_id = json.get("job_id");
if (job_id.kind != JSON_INTEGER) {
std::cerr << "Expected integer for 'job_id' key" << std::endl;
return false;
}

out = {type, std::stoi(job_id.value)};

return true;
}
};
182 changes: 182 additions & 0 deletions tools/evict-shared-cache/evict-shared-cache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*
* Copyright 2023 SiFive, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of LICENSE.Apache2 along with
* this software. If not, you may obtain a copy at
*
* https://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.
*/

// Open Group Base Specifications Issue 7
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L

#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>

#include "command.h"
#include "eviction-policy.h"
#include "gopt/gopt-arg.h"
#include "gopt/gopt.h"
#include "util/poll.h"

void print_help(const char* argv0) {
// clang-format off
std::cerr << std::endl
<< "Usage: " << argv0 << " [OPTIONS]" << std::endl
<< " --cache DIR Evict from shared cache DIR" << std::endl
<< " --policy POLICY Evict using POLICY" << std::endl
<< " --help -h Print this message and exit" << std::endl
<< "Commands (read from stdin):" << std::endl
<< " write JOB_ID JOB_ID was written into the shared cache" << std::endl
<< " read JOB_ID JOB_ID was read from the shared cache" << std::endl
<< "Available Policies:" << std::endl
<< " nil No op policy. Process commands but do nothing." << std::endl
<< std::endl;
// clang-format on
}

std::unique_ptr<EvictionPolicy> make_policy(const char* argv0, const char* policy) {
if (strcmp(policy, "nil") == 0) {
return std::make_unique<NilEvictionPolicy>();
}

std::cerr << "Unknown policy: " << policy << std::endl;
print_help(argv0);
exit(EXIT_FAILURE);
}

enum class CommandParserState { Continue, StopSuccess, StopFail };

struct CommandParser {
std::string command_buff = "";
Poll poll;

CommandParser() { poll.add(STDIN_FILENO); }

CommandParserState read_commands(std::vector<std::string>& commands) {
commands = {};

// Sleep until a signal arrives
sigset_t saved;
/* std::vector<int> ready_fds = */ poll.wait(nullptr, &saved);

while (true) {
uint8_t buffer[4096] = {};

ssize_t count = read(STDIN_FILENO, static_cast<void*>(buffer), 4096);

// Nothing new to process, yield control
if (count == 0) {
return CommandParserState::Continue;
}

// An error occured during read
if (count < 0) {
// EBADF means that stdin was closed. This is the signal to stop
// processing commands.
if (errno == EBADF) {
return CommandParserState::StopSuccess;
}

std::cerr << "Failed to read from stdin: " << strerror(errno) << std::endl;
return CommandParserState::StopFail;
}

uint8_t* iter = buffer;
uint8_t* buffer_end = buffer + count;
while (iter < buffer_end) {
auto end = std::find(iter, buffer_end, 0);
command_buff.append(iter, end);
if (end != buffer_end) {
commands.emplace_back(std::move(command_buff));
command_buff = "";
}
iter = end + 1;
}
}

// not actually reachable
return CommandParserState::Continue;
}
};

int main(int argc, char** argv) {
// clang-format off
struct option options[] {
{0, "cache", GOPT_ARGUMENT_REQUIRED},
{0, "policy", GOPT_ARGUMENT_REQUIRED},
{'h', "help", GOPT_ARGUMENT_FORBIDDEN},
{0, 0, GOPT_LAST}
};
// clang-format on

argc = gopt(argv, options);
gopt_errors(argv[0], options);

bool help = arg(options, "help")->count;
const char* cache = arg(options, "cache")->argument;
const char* policy_name = arg(options, "policy")->argument;

if (help) {
print_help(argv[0]);
exit(EXIT_SUCCESS);
}

if (!cache) {
std::cerr << "Cache directory not specified" << std::endl;
print_help(argv[0]);
exit(EXIT_FAILURE);
}

if (!policy_name) {
std::cerr << "Eviction policy not specified" << std::endl;
print_help(argv[0]);
exit(EXIT_FAILURE);
}

std::unique_ptr<EvictionPolicy> policy = make_policy(argv[0], policy_name);
policy->init();

CommandParser cmd_parser;
CommandParserState state;
do {
std::vector<std::string> cmds;
state = cmd_parser.read_commands(cmds);

for (const auto& c : cmds) {
Command cmd;
if (!Command::parse(c, cmd)) {
exit(EXIT_FAILURE);
}

switch (cmd.type) {
case CommandType::Read:
policy->read(cmd.job_id);
break;
case CommandType::Write:
policy->write(cmd.job_id);
break;
default:
std::cerr << "Unhandled command type" << std::endl;
exit(EXIT_FAILURE);
}
}
} while (state == CommandParserState::Continue);

exit(state == CommandParserState::StopSuccess ? EXIT_SUCCESS : EXIT_FAILURE);
}
20 changes: 20 additions & 0 deletions tools/evict-shared-cache/evict-shared-cache.wake
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2023 SiFive, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You should have received a copy of LICENSE.Apache2 along with
# this software. If not, you may obtain a copy at
#
# https://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.

package build_wake
from wake import _

target buildEvictSharedCache variant =
tool here Nil variant "bin/evict-shared-cache" (gopt, json, util,) Nil Nil
43 changes: 43 additions & 0 deletions tools/evict-shared-cache/eviction-policy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2022 SiFive, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You should have received a copy of LICENSE.Apache2 along with
* this software. If not, you may obtain a copy at
*
* https://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.
*/

#pragma once

// Open Group Base Specifications Issue 7
#define _XOPEN_SOURCE 700
#define _POSIX_C_SOURCE 200809L

#include <iostream>
#include <string>

struct EvictionPolicy {
virtual void init() = 0;
virtual void read(int id) = 0;
virtual void write(int id) = 0;
};

struct NilEvictionPolicy : EvictionPolicy {
virtual void init() override { std::cerr << "NilEvictionPolicy::init()" << std::endl; }

virtual void read(int id) override {
std::cerr << "NilEvictionPolicy::read(" << id << ")" << std::endl;
}

virtual void write(int id) override {
std::cerr << "NilEvictionPolicy::write(" << id << ")" << std::endl;
}
};
1 change: 1 addition & 0 deletions wake.spec.in
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ mkdir -p %{buildroot}/var/cache/wake
/usr/bin/wake
/usr/bin/wakebox
/usr/bin/wake-format
/usr/bin/evict-shared-cache
/usr/lib/wake
/usr/share/wake
%dir /var/cache/wake
Expand Down

0 comments on commit b1aa8eb

Please sign in to comment.