1/*
2 * Copyright (c) 2014, ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9#define _GNU_SOURCE
10
11#include <stdlib.h>
12#include <stdio.h>
13#include <stdint.h>
14#include <string.h>
15#include <assert.h>
16#include <stdbool.h>
17#include <sys/time.h>
18
19#include <sched.h> /* CPU_SET et. al. */
20#include <pthread.h>
21
22#ifdef BARRELFISH
23#include <barrelfish/barrelfish.h>
24#include <octopus/octopus.h>
25#else
26#define MAX_COREID 1024
27#define USER_PANIC_ERR(x...)
28#define USER_PANIC(x...)
29#define err_is_fail(x) false
30typedef size_t errval_t;
31#endif
32
33#define NUM_INCR 1000000
34static size_t sanity = 0;
35static size_t cpu_count = 4;
36
37static void* prj_thread(void* param)
38{
39#if BARRELFISH
40    printf("Hello from thread %"PRIu64" on core %"PRIuCOREID"\n",
41           (uint64_t)param, disp_get_core_id());
42#endif
43    sanity = 1;
44    //messages_handler_loop();
45    return 0;
46}
47
48static int pthread_create_join_test(void) {
49    pthread_t tid;
50    pthread_attr_t attr;
51    pthread_attr_init(&attr);
52
53    for (size_t rep = 0; rep < 20; rep++) {
54        printf("Create a new thread rep=%zu\n", rep);
55        int rv = pthread_create(&tid, &attr, prj_thread, NULL);
56        if (rv){
57            printf("[ERROR] return code from pthread_create() is %d\n", rv);
58            return 1;
59        }
60
61        /* wait for threads to finish */
62        pthread_join(tid, NULL);
63
64        assert(sanity == 1);
65        sanity = 0;
66        printf("Joined thread.\n");
67    }
68
69    printf("%s PASS\n", __FUNCTION__);
70    return 0;
71}
72
73static int pthread_setaffinity_test(void) {
74
75    pthread_t tid;
76    pthread_attr_t attr;
77    pthread_attr_init(&attr);
78    cpu_set_t set;
79
80    for (size_t rep = 0; rep < cpu_count; rep++) {
81        printf("Create a new thread on core %zu\n", rep);
82        sanity = 0;
83
84        CPU_ZERO(&set);
85        CPU_SET(rep, &set);
86        pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &set);
87        int rv = pthread_create(&tid, &attr, prj_thread, NULL);
88        if (rv){
89            printf("[ERROR] return code from pthread_create() is %d\n", rv);
90            return 1;
91        }
92
93        /* wait for threads to finish */
94        pthread_join(tid, NULL);
95        //assert(sanity == 1);
96    }
97
98    printf("%s PASS\n", __FUNCTION__);
99    return 0;
100}
101
102static pthread_mutex_t lock;
103
104static size_t atomic_counter;
105static void* mutex_increment(void* param)
106{
107    struct timeval start;
108    struct timeval end;
109
110    gettimeofday(&start, NULL);
111    for (size_t i=0; i < NUM_INCR; i++) {
112        pthread_mutex_lock(&lock);
113        if (atomic_counter % 10000 == 0) {
114            debug_printf("progress\n");
115        }
116        atomic_counter++;
117        pthread_mutex_unlock(&lock);
118    }
119    gettimeofday(&end, NULL);
120
121    assert(atomic_counter >= NUM_INCR);
122    double diff_usec = (((end).tv_sec*1000000L + (end).tv_usec) - ((start).tv_sec*1000000L+(start).tv_usec));
123
124    debug_printf("%s:%s:%d: end-start [usec] = %.4lf\n",
125                 __FILE__, __FUNCTION__, __LINE__, diff_usec);
126
127    return 0;
128}
129
130static int pthread_mutex_performance(void) {
131    pthread_t tid[cpu_count];
132    pthread_attr_t attr;
133    pthread_attr_init(&attr);
134    pthread_mutex_init(&lock, NULL);
135    cpu_set_t set;
136
137    for (size_t rep = 0; rep < cpu_count; rep++) {
138        printf("Create a new thread on core %zu\n", rep);
139        CPU_ZERO(&set);
140        CPU_SET(rep, &set);
141        pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &set);
142        int rv = pthread_create(&tid[rep], &attr, mutex_increment, NULL);
143        if (rv){
144            printf("[ERROR] return code from pthread_create() is %d\n", rv);
145            return 1;
146        }
147    }
148    for (size_t rep = 0; rep < cpu_count; rep++) {
149        printf("%s:%s:%d: waiting for %zu\n", __FILE__, __FUNCTION__, __LINE__, rep);
150        pthread_join(tid[rep], NULL);
151    }
152    assert(atomic_counter == cpu_count*NUM_INCR);
153
154
155    printf("%s PASS\n", __FUNCTION__);
156
157    return 0;
158}
159
160static bool is_spanned[MAX_COREID] = { false };
161
162static void domain_spanned_callback(void *arg, errval_t err)
163{
164    if (err_is_fail(err)) {
165        USER_PANIC_ERR(err, "domain spanning failed.");
166    }
167    uintptr_t i = (uintptr_t) arg;
168    is_spanned[i] = true;
169}
170
171int main(int argc, char** argv)
172{
173    int r = 0;
174#ifdef BARRELFISH
175    oct_init();
176    static char* local_apics = "r'hw\\.processor\\.[0-9]+' { enabled: 1 }";
177    char** names;
178    errval_t err = oct_get_names(&names, &cpu_count, local_apics);
179    if (err_is_fail(err)) {
180        DEBUG_ERR(err, "Can not get core count");
181        return 1;
182    }
183    oct_free_names(names, cpu_count);
184
185    // Spawn to other cores:
186    for (size_t rep = 0; rep < cpu_count; rep++) {
187        if (rep != disp_get_core_id()) {
188            err = domain_new_dispatcher(rep, domain_spanned_callback, (void*)(uintptr_t)rep);
189            if (err_is_fail(err)) {
190                USER_PANIC_ERR(err, "domain_new_dispatcher failed");
191            }
192
193            while (!is_spanned[rep]) {
194                thread_yield();
195            }
196        }
197    }
198#endif
199
200    r = pthread_mutex_performance();
201    if (r != 0) {
202        USER_PANIC("pthread_mutex_performance failed");
203    }
204
205    r = pthread_setaffinity_test();
206    if (r != 0) {
207        USER_PANIC("pthread_setaffinity_test failed");
208    }
209
210    r = pthread_create_join_test();
211    if (r != 0) {
212        USER_PANIC("pthread_create_join_test failed");
213    }
214
215    printf("TESTS PASSED\n");
216
217#ifdef BARRELFISH
218    // XXX: threads on remote cores page-fault if parent exits :(
219    messages_handler_loop();
220#endif
221    return 0;
222}