
hash.h depends upon and includes repository.h, due to the definition and use of the_hash_algo (defined as the_repository->hash_algo). However, most headers trying to include hash.h are only interested in the layout of the structs like object_id. Move the parts of hash.h that do not depend upon repository.h into a new file hash-ll.h (the "low level" parts of hash.h), and adjust other files to use this new header where the convenience inline functions aren't needed. This allows hash.h and object.h to be fairly small, minimal headers. It also exposes a lot of hidden dependencies on both path.h (which was brought in by repository.h) and repository.h (which was previously implicitly brought in by object.h), so also adjust other files to be more explicit about what they depend upon. Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
226 lines
5.1 KiB
C
226 lines
5.1 KiB
C
/*
|
|
* test-fsmonitor-client.c: client code to send commands/requests to
|
|
* a `git fsmonitor--daemon` daemon.
|
|
*/
|
|
|
|
#include "test-tool.h"
|
|
#include "cache.h"
|
|
#include "parse-options.h"
|
|
#include "fsmonitor-ipc.h"
|
|
#include "repository.h"
|
|
#include "setup.h"
|
|
#include "thread-utils.h"
|
|
#include "trace2.h"
|
|
#include "wrapper.h"
|
|
|
|
#ifndef HAVE_FSMONITOR_DAEMON_BACKEND
|
|
int cmd__fsmonitor_client(int argc, const char **argv)
|
|
{
|
|
die("fsmonitor--daemon not available on this platform");
|
|
}
|
|
#else
|
|
|
|
/*
|
|
* Read the `.git/index` to get the last token written to the
|
|
* FSMonitor Index Extension.
|
|
*/
|
|
static const char *get_token_from_index(void)
|
|
{
|
|
struct index_state *istate = the_repository->index;
|
|
|
|
if (do_read_index(istate, the_repository->index_file, 0) < 0)
|
|
die("unable to read index file");
|
|
if (!istate->fsmonitor_last_update)
|
|
die("index file does not have fsmonitor extension");
|
|
|
|
return istate->fsmonitor_last_update;
|
|
}
|
|
|
|
/*
|
|
* Send an IPC query to a `git-fsmonitor--daemon` daemon and
|
|
* ask for the changes since the given token or from the last
|
|
* token in the index extension.
|
|
*
|
|
* This will implicitly start a daemon process if necessary. The
|
|
* daemon process will persist after we exit.
|
|
*/
|
|
static int do_send_query(const char *token)
|
|
{
|
|
struct strbuf answer = STRBUF_INIT;
|
|
int ret;
|
|
|
|
if (!token || !*token)
|
|
token = get_token_from_index();
|
|
|
|
ret = fsmonitor_ipc__send_query(token, &answer);
|
|
if (ret < 0)
|
|
die("could not query fsmonitor--daemon");
|
|
|
|
write_in_full(1, answer.buf, answer.len);
|
|
strbuf_release(&answer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Send a "flush" command to the `git-fsmonitor--daemon` (if running)
|
|
* and tell it to flush its cache.
|
|
*
|
|
* This feature is primarily used by the test suite to simulate a loss of
|
|
* sync with the filesystem where we miss kernel events.
|
|
*/
|
|
static int do_send_flush(void)
|
|
{
|
|
struct strbuf answer = STRBUF_INIT;
|
|
int ret;
|
|
|
|
ret = fsmonitor_ipc__send_command("flush", &answer);
|
|
if (ret)
|
|
return ret;
|
|
|
|
write_in_full(1, answer.buf, answer.len);
|
|
strbuf_release(&answer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct hammer_thread_data
|
|
{
|
|
pthread_t pthread_id;
|
|
int thread_nr;
|
|
|
|
int nr_requests;
|
|
const char *token;
|
|
|
|
int sum_successful;
|
|
int sum_errors;
|
|
};
|
|
|
|
static void *hammer_thread_proc(void *_hammer_thread_data)
|
|
{
|
|
struct hammer_thread_data *data = _hammer_thread_data;
|
|
struct strbuf answer = STRBUF_INIT;
|
|
int k;
|
|
int ret;
|
|
|
|
trace2_thread_start("hammer");
|
|
|
|
for (k = 0; k < data->nr_requests; k++) {
|
|
strbuf_reset(&answer);
|
|
|
|
ret = fsmonitor_ipc__send_query(data->token, &answer);
|
|
if (ret < 0)
|
|
data->sum_errors++;
|
|
else
|
|
data->sum_successful++;
|
|
}
|
|
|
|
strbuf_release(&answer);
|
|
trace2_thread_exit();
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Start a pool of client threads that will each send a series of
|
|
* commands to the daemon.
|
|
*
|
|
* The goal is to overload the daemon with a sustained series of
|
|
* concurrent requests.
|
|
*/
|
|
static int do_hammer(const char *token, int nr_threads, int nr_requests)
|
|
{
|
|
struct hammer_thread_data *data = NULL;
|
|
int k;
|
|
int sum_join_errors = 0;
|
|
int sum_commands = 0;
|
|
int sum_errors = 0;
|
|
|
|
if (!token || !*token)
|
|
token = get_token_from_index();
|
|
if (nr_threads < 1)
|
|
nr_threads = 1;
|
|
if (nr_requests < 1)
|
|
nr_requests = 1;
|
|
|
|
CALLOC_ARRAY(data, nr_threads);
|
|
|
|
for (k = 0; k < nr_threads; k++) {
|
|
struct hammer_thread_data *p = &data[k];
|
|
p->thread_nr = k;
|
|
p->nr_requests = nr_requests;
|
|
p->token = token;
|
|
|
|
if (pthread_create(&p->pthread_id, NULL, hammer_thread_proc, p)) {
|
|
warning("failed to create thread[%d] skipping remainder", k);
|
|
nr_threads = k;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (k = 0; k < nr_threads; k++) {
|
|
struct hammer_thread_data *p = &data[k];
|
|
|
|
if (pthread_join(p->pthread_id, NULL))
|
|
sum_join_errors++;
|
|
sum_commands += p->sum_successful;
|
|
sum_errors += p->sum_errors;
|
|
}
|
|
|
|
fprintf(stderr, "HAMMER: [threads %d][requests %d] [ok %d][err %d][join %d]\n",
|
|
nr_threads, nr_requests, sum_commands, sum_errors, sum_join_errors);
|
|
|
|
free(data);
|
|
|
|
/*
|
|
* Return an error if any of the _send_query requests failed.
|
|
* We don't care about thread create/join errors.
|
|
*/
|
|
return sum_errors > 0;
|
|
}
|
|
|
|
int cmd__fsmonitor_client(int argc, const char **argv)
|
|
{
|
|
const char *subcmd;
|
|
const char *token = NULL;
|
|
int nr_threads = 1;
|
|
int nr_requests = 1;
|
|
|
|
const char * const fsmonitor_client_usage[] = {
|
|
"test-tool fsmonitor-client query [<token>]",
|
|
"test-tool fsmonitor-client flush",
|
|
"test-tool fsmonitor-client hammer [<token>] [<threads>] [<requests>]",
|
|
NULL,
|
|
};
|
|
|
|
struct option options[] = {
|
|
OPT_STRING(0, "token", &token, "token",
|
|
"command token to send to the server"),
|
|
|
|
OPT_INTEGER(0, "threads", &nr_threads, "number of client threads"),
|
|
OPT_INTEGER(0, "requests", &nr_requests, "number of requests per thread"),
|
|
|
|
OPT_END()
|
|
};
|
|
|
|
argc = parse_options(argc, argv, NULL, options, fsmonitor_client_usage, 0);
|
|
|
|
if (argc != 1)
|
|
usage_with_options(fsmonitor_client_usage, options);
|
|
|
|
subcmd = argv[0];
|
|
|
|
setup_git_directory();
|
|
|
|
if (!strcmp(subcmd, "query"))
|
|
return !!do_send_query(token);
|
|
|
|
if (!strcmp(subcmd, "flush"))
|
|
return !!do_send_flush();
|
|
|
|
if (!strcmp(subcmd, "hammer"))
|
|
return !!do_hammer(token, nr_threads, nr_requests);
|
|
|
|
die("Unhandled subcommand: '%s'", subcmd);
|
|
}
|
|
#endif
|