blob: 591096c714f8daecb82ad649e7cc8cfca579628e [file] [log] [blame]
/*
* Copyright (c) 2015 Carlos Pizano-Uribe cpu@chromium.org
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <debug.h>
#include <err.h>
#include <rand.h>
#include <string.h>
#include <trace.h>
#include <kernel/port.h>
#include <kernel/thread.h>
#include <platform.h>
#define LOCAL_TRACE 0
void* context1 = (void*) 0x53;
static void dump_port_result(const port_result_t* result)
{
const port_packet_t* p = &result->packet;
LTRACEF("[%02x %02x %02x %02x %02x %02x %02x %02x]\n",
p->value[0], p->value[1], p->value[2], p->value[3],
p->value[4], p->value[5], p->value[6], p->value[7]);
}
static int single_thread_basic(void)
{
port_t w_port;
status_t st = port_create("sh_prt1", PORT_MODE_UNICAST, &w_port);
if (st < 0) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
port_t r_port;
st = port_open("sh_prt0", context1, &r_port);
if (st != ERR_NOT_FOUND) {
printf("expected not to find port, status = %d\n", st);
return __LINE__;
}
st = port_open("sh_prt1", context1, &r_port);
if (st < 0) {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
port_packet_t packet[3] = {
{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}},
{{0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11}},
{{0x33, 0x66, 0x99, 0xcc, 0x33, 0x66, 0x99, 0xcc}},
};
st = port_write(w_port, &packet[0], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
printf("reading from port:\n");
port_result_t res = {0};
st = port_read(r_port, 0, &res);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
if (res.ctx != context1) {
printf("bad context! = %p\n", res.ctx);
return __LINE__;
}
st = port_read(r_port, 0, &res);
if (st != ERR_TIMED_OUT) {
printf("expected timeout, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet[1], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet[0], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
st = port_write(w_port, &packet[2], 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
int expected_count = 3;
while (true) {
st = port_read(r_port, 0, &res);
if (st < 0)
break;
dump_port_result(&res);
--expected_count;
}
if (expected_count != 0) {
printf("invalid read count = %d\n", expected_count);
return __LINE__;
}
printf("\n");
// port should be empty. should be able to write 8 packets.
expected_count = 8;
while (true) {
st = port_write(w_port, &packet[1], 1);
if (st < 0)
break;
--expected_count;
st = port_write(w_port, &packet[2], 1);
if (st < 0)
break;
--expected_count;
}
if (expected_count != 0) {
printf("invalid write count = %d\n", expected_count);
return __LINE__;
}
// tod(cpu) fix this possibly wrong error.
if (st != ERR_PARTIAL_WRITE) {
printf("expected buffer error, status =%d\n", st);
return __LINE__;
}
// read 3 packets.
for (int ix = 0; ix != 3; ++ix) {
st = port_read(r_port, 0, &res);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
}
// there are 5 packets, now we add another 3.
st = port_write(w_port, packet, 3);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
expected_count = 8;
while (true) {
st = port_read(r_port, 0, &res);
if (st < 0)
break;
dump_port_result(&res);
--expected_count;
}
if (expected_count != 0) {
printf("invalid read count = %d\n", expected_count);
return __LINE__;
}
// attempt to use the wrong port.
st = port_write(r_port, &packet[1], 1);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_read(w_port, 0, &res);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_close(r_port);
if (st < 0) {
printf("could not close read port, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st < 0) {
printf("could not close write port, status = %d\n", st);
return __LINE__;
}
st = port_close(r_port);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st != ERR_BAD_HANDLE) {
printf("expected bad handle error, status = %d\n", st);
return __LINE__;
}
st = port_destroy(w_port);
if (st < 0) {
printf("could not destroy port, status = %d\n", st);
return __LINE__;
}
printf("single_thread_basic : ok\n");
return 0;
}
static int ping_pong_thread(void *arg)
{
port_t r_port;
status_t st = port_open("ping_port", NULL, &r_port);
if (st < 0) {
printf("thread: could not open port, status = %d\n", st);
return __LINE__;
}
bool should_dispose_pong_port = true;
port_t w_port;
st = port_create("pong_port", PORT_MODE_UNICAST, &w_port);
if (st == ERR_ALREADY_EXISTS) {
// won the race to create the port.
should_dispose_pong_port = false;
} else if (st < 0) {
printf("thread: could not open port, status = %d\n", st);
return __LINE__;
}
port_result_t pr;
// the loop is read-mutate-write until the write port
// is closed by the master thread.
while (true) {
st = port_read(r_port, INFINITE_TIME, &pr);
if (st == ERR_CANCELLED) {
break;
} else if (st < 0) {
printf("thread: could not read port, status = %d\n", st);
return __LINE__;
}
pr.packet.value[0]++;
pr.packet.value[5]--;
st = port_write(w_port, &pr.packet, 1);
if (st < 0) {
printf("thread: could not write port, status = %d\n", st);
return __LINE__;
}
}
port_close(r_port);
if (should_dispose_pong_port) {
port_close(w_port);
port_destroy(w_port);
}
return 0;
bail:
return __LINE__;
}
int two_threads_basic(void)
{
port_t w_port;
status_t st = port_create("ping_port", PORT_MODE_BROADCAST, &w_port);
if (st < 0) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
thread_t* t1 = thread_create(
"worker1", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_t* t2 = thread_create(
"worker2", &ping_pong_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(t1);
thread_resume(t2);
// wait for the pong port to be created, the two threads race to do it.
port_t r_port;
while (true) {
status_t st = port_open("pong_port", NULL, &r_port);
if (st == NO_ERROR) {
break;
} else if (st == ERR_NOT_FOUND) {
thread_sleep(100);
} else {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
}
// We have two threads listening to the ping port. Which both reply
// on the pong port, so we get two packets in per packet out.
const int passes = 256;
printf("two_threads_basic test, %d passes\n", passes);
port_packet_t packet_out = {{0xaf, 0x77, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05}};
port_result_t pr;
for (int ix = 0; ix != passes; ++ix) {
const size_t count = 1 + ((unsigned int)rand() % 3);
for (size_t jx = 0; jx != count; ++jx) {
st = port_write(w_port, &packet_out, 1);
if (st < 0) {
printf("could not write port, status = %d\n", st);
return __LINE__;
}
}
packet_out.value[0]++;
packet_out.value[5]--;
for (size_t jx = 0; jx != count * 2; ++jx) {
st = port_read(r_port, INFINITE_TIME, &pr);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
if ((pr.packet.value[0] != packet_out.value[0]) ||
(pr.packet.value[5] != packet_out.value[5])) {
printf("unexpected data in packet, loop %d", ix);
return __LINE__;
}
}
}
thread_sleep(100);
// there should be no more packets to read.
st = port_read(r_port, 0, &pr);
if (st != ERR_TIMED_OUT) {
printf("unexpected packet, status = %d\n", st);
return __LINE__;
}
printf("two_threads_basic master shutdown\n");
st = port_close(r_port);
if (st < 0) {
printf("could not close port, status = %d\n", st);
return __LINE__;
}
st = port_close(w_port);
if (st < 0) {
printf("could not close port, status = %d\n", st);
return __LINE__;
}
st = port_destroy(w_port);
if (st < 0) {
printf("could not destroy port, status = %d\n", st);
return __LINE__;
}
int retcode = -1;
thread_join(t1, &retcode, INFINITE_TIME);
if (retcode)
goto fail;
thread_join(t2, &retcode, INFINITE_TIME);
if (retcode)
goto fail;
return 0;
fail:
printf("child thread exited with %d\n", retcode);
return __LINE__;
}
#define CMD_PORT_CTX ((void*) 0x77)
#define TS1_PORT_CTX ((void*) 0x11)
#define TS2_PORT_CTX ((void*) 0x12)
typedef enum {
ADD_PORT,
QUIT
} action_t;
typedef struct {
action_t what;
port_t port;
} watcher_cmd;
status_t send_watcher_cmd(port_t cmd_port, action_t action, port_t port)
{
watcher_cmd cmd = {action, port};
return port_write(cmd_port, ((port_packet_t*) &cmd), 1);;
}
static int group_watcher_thread(void *arg)
{
port_t watched[8] = {0};
status_t st = port_open("grp_ctrl", CMD_PORT_CTX, &watched[0]);
if (st < 0) {
printf("could not open port, status = %d\n", st);
return __LINE__;
}
size_t count = 1;
port_t group;
int ctx_count = -1;
while (true) {
st = port_group(watched, count, &group);
if (st < 0) {
printf("could not make group, status = %d\n", st);
return __LINE__;
}
port_result_t pr;
while (true) {
st = port_read(group, INFINITE_TIME, &pr);
if (st < 0) {
printf("could not read port, status = %d\n", st);
return __LINE__;
}
if (pr.ctx == CMD_PORT_CTX) {
break;
} else if (pr.ctx == TS1_PORT_CTX) {
ctx_count += 1;
} else if (pr.ctx == TS2_PORT_CTX) {
ctx_count += 2;
} else {
printf("unknown context %p\n", pr.ctx);
return __LINE__;
}
}
// Either adding a port or exiting; either way close the
// existing group port and create a new one if needed
// at the top of the loop.
port_close(group);
watcher_cmd* wc = (watcher_cmd*) &pr.packet;
if (wc->what == ADD_PORT) {
watched[count++] = wc->port;
} else if (wc->what == QUIT) {
break;
} else {
printf("unknown command %d\n", wc->what);
return __LINE__;
}
}
if (ctx_count != 2) {
printf("unexpected context count %d", ctx_count);
return __LINE__;
}
printf("group watcher shutdown\n");
for (size_t ix = 0; ix != count; ++ix) {
st = port_close(watched[ix]);
if (st < 0) {
printf("failed to close read port, status = %d\n", st);
return __LINE__;
}
}
return 0;
}
static status_t make_port_pair(const char* name, void* ctx, port_t* write, port_t* read)
{
status_t st = port_create(name, PORT_MODE_UNICAST, write);
if (st < 0)
return st;
return port_open(name,ctx, read);
}
int group_basic(void)
{
// we spin a thread that connects to a well known port, then we
// send two ports that it will add to a group port.
port_t cmd_port;
status_t st = port_create("grp_ctrl", PORT_MODE_UNICAST, &cmd_port);
if (st < 0 ) {
printf("could not create port, status = %d\n", st);
return __LINE__;
}
thread_t* wt = thread_create(
"g_watcher", &group_watcher_thread, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_resume(wt);
port_t w_test_port1, r_test_port1;
st = make_port_pair("tst_port1", TS1_PORT_CTX, &w_test_port1, &r_test_port1);
if (st < 0)
return __LINE__;
port_t w_test_port2, r_test_port2;
st = make_port_pair("tst_port2", TS2_PORT_CTX, &w_test_port2, &r_test_port2);
if (st < 0)
return __LINE__;
st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port1);
if (st < 0)
return __LINE__;
st = send_watcher_cmd(cmd_port, ADD_PORT, r_test_port2);
if (st < 0)
return __LINE__;
thread_sleep(50);
port_packet_t pp = {{0}};
st = port_write(w_test_port1, &pp, 1);
if (st < 0)
return __LINE__;
st = port_write(w_test_port2, &pp, 1);
if (st < 0)
return __LINE__;
st = send_watcher_cmd(cmd_port, QUIT, 0);
if (st < 0)
return __LINE__;
int retcode = -1;
thread_join(wt, &retcode, INFINITE_TIME);
if (retcode) {
printf("child thread exited with %d\n", retcode);
return __LINE__;
}
st = port_close(w_test_port1);
if (st < 0)
return __LINE__;
st = port_close(w_test_port2);
if (st < 0)
return __LINE__;
st = port_close(cmd_port);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port1);
if (st < 0)
return __LINE__;
st = port_destroy(w_test_port2);
if (st < 0)
return __LINE__;
st = port_destroy(cmd_port);
if (st < 0)
return __LINE__;
return 0;
}
#define RUN_TEST(t) result = t(); if (result) goto fail
int port_tests(void)
{
int result;
int count = 3;
while (count--) {
RUN_TEST(single_thread_basic);
RUN_TEST(two_threads_basic);
RUN_TEST(group_basic);
}
printf("all tests passed\n");
return 0;
fail:
printf("test failed at line %d\n", result);
return 1;
}
#undef RUN_TEST