Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002  * Copyright (c) 2013-2017, ARM Limited, All Rights Reserved
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License"); you may
00006  * not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  * http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018  #ifndef MBED_CONF_APP_CONNECT_STATEMENT
00019      #error [NOT_SUPPORTED] No network configuration found for this target.
00020  #endif
00021 
00022 #ifndef MBED_EXTENDED_TESTS
00023     #error [NOT_SUPPORTED] Parallel pressure tests are not supported by default
00024 #endif
00025 
00026 #include "mbed.h"
00027 #include MBED_CONF_APP_HEADER_FILE
00028 #include "UDPSocket.h"
00029 #include "greentea-client/test_env.h"
00030 #include "unity/unity.h"
00031 #include "utest.h"
00032 
00033 using namespace utest::v1;
00034 
00035 
00036 #ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN
00037 #define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN 64
00038 #endif
00039 
00040 #ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX
00041 #define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX 0x80000
00042 #endif
00043 
00044 #ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT
00045 #define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT 100
00046 #endif
00047 
00048 #ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED
00049 #define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED 0x6d626564
00050 #endif
00051 
00052 #ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS
00053 #define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS 3
00054 #endif
00055 
00056 #ifndef MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG
00057 #define MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG false
00058 #endif
00059 
00060 #define STRINGIZE(x) STRINGIZE2(x)
00061 #define STRINGIZE2(x) #x
00062 
00063 
00064 // Simple xorshift pseudorandom number generator
00065 class RandSeq {
00066 private:
00067     uint32_t x;
00068     uint32_t y;
00069     static const int A = 15;
00070     static const int B = 18;
00071     static const int C = 11;
00072 
00073 public:
00074     RandSeq(uint32_t seed=MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_SEED)
00075         : x(seed), y(seed) {}
00076 
00077     uint32_t next(void) {
00078         x ^= x << A;
00079         x ^= x >> B;
00080         x ^= y ^ (y >> C);
00081         return x + y;
00082     }
00083 
00084     void skip(size_t size) {
00085         for (size_t i = 0; i < size; i++) {
00086             next();
00087         }
00088     }
00089 
00090     void buffer(uint8_t *buffer, size_t size) {
00091         RandSeq lookahead = *this;
00092 
00093         for (size_t i = 0; i < size; i++) {
00094             buffer[i] = lookahead.next() & 0xff;
00095         }
00096     }
00097 
00098     int cmp(uint8_t *buffer, size_t size) {
00099         RandSeq lookahead = *this;
00100 
00101         for (size_t i = 0; i < size; i++) {
00102             int diff = buffer[i] - (lookahead.next() & 0xff);
00103             if (diff != 0) {
00104                 return diff;
00105             }
00106         }
00107         return 0;
00108     }
00109 };
00110 
00111 // Tries to get the biggest buffer possible on the device. Exponentially
00112 // grows a buffer until heap runs out of space, and uses half to leave
00113 // space for the rest of the program
00114 void generate_buffer(uint8_t **buffer, size_t *size, size_t min, size_t max) {
00115     size_t i = min;
00116     while (i < max) {
00117         void *b = malloc(i);
00118         if (!b) {
00119             i /= 8;
00120             if (i < min) {
00121                 i = min;
00122             }
00123             break;
00124         }
00125         free(b);
00126         i *= 2;
00127     }
00128 
00129     *buffer = (uint8_t *)malloc(i);
00130     *size = i;
00131     TEST_ASSERT(buffer);
00132 }
00133 
00134 
00135 // Global variables shared between pressure tests
00136 NetworkInterface* net;
00137 SocketAddress udp_addr;
00138 Timer timer;
00139 Mutex iomutex;
00140 
00141 // Single instance of a pressure test
00142 class PressureTest {
00143 private:
00144     uint8_t *buffer;
00145     size_t buffer_size;
00146 
00147     UDPSocket sock;
00148     Thread thread;
00149 
00150 public:
00151     PressureTest(uint8_t *buffer, size_t buffer_size)
00152         : buffer(buffer), buffer_size(buffer_size) {
00153     }
00154 
00155     void start() {
00156         osStatus status = thread.start(callback(this, &PressureTest::run));
00157         TEST_ASSERT_EQUAL(osOK, status);
00158     }
00159 
00160     void join() {
00161         osStatus status = thread.join();
00162         TEST_ASSERT_EQUAL(osOK, status);
00163     }
00164 
00165     void run() {
00166         // Tests exponentially growing sequences
00167         for (size_t size = MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN;
00168              size < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX;
00169              size *= 2) {
00170             int err = sock.open(net);
00171             TEST_ASSERT_EQUAL(0, err);
00172             iomutex.lock();
00173             printf("UDP: %s:%d streaming %d bytes\r\n",
00174                 udp_addr.get_ip_address(), udp_addr.get_port(), size);
00175             iomutex.unlock();
00176 
00177             sock.set_blocking(false);
00178 
00179             // Loop to send/recv all data
00180             RandSeq tx_seq;
00181             RandSeq rx_seq;
00182             size_t rx_count = 0;
00183             size_t tx_count = 0;
00184             int known_time = timer.read_ms();
00185             size_t window = buffer_size;
00186 
00187             while (tx_count < size || rx_count < size) {
00188                 // Send out packets
00189                 if (tx_count < size) {
00190                     size_t chunk_size = size - tx_count;
00191                     if (chunk_size > window) {
00192                         chunk_size = window;
00193                     }
00194 
00195                     tx_seq.buffer(buffer, chunk_size);
00196                     int td = sock.sendto(udp_addr, buffer, chunk_size);
00197 
00198                     if (td > 0) {
00199                         if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
00200                             iomutex.lock();
00201                             printf("UDP: tx -> %d\r\n", td);
00202                             iomutex.unlock();
00203                         }
00204                         tx_seq.skip(td);
00205                         tx_count += td;
00206                     } else if (td != NSAPI_ERROR_WOULD_BLOCK ) {
00207                         // We may fail to send because of buffering issues, revert to
00208                         // last good sequence and cut buffer in half
00209                         if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) {
00210                             window /= 2;
00211                         }
00212 
00213                         if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
00214                             iomutex.lock();
00215                             printf("UDP: Not sent (%d), window = %d\r\n", td, window);
00216                             iomutex.unlock();
00217                         }
00218                     }
00219                 }
00220 
00221                 // Prioritize recieving over sending packets to avoid flooding
00222                 // the network while handling erronous packets
00223                 while (rx_count < size) {
00224                     int rd = sock.recvfrom(NULL, buffer, buffer_size);
00225                     TEST_ASSERT(rd > 0 || rd == NSAPI_ERROR_WOULD_BLOCK );
00226 
00227                     if (rd > 0) {
00228                         if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
00229                             iomutex.lock();
00230                             printf("UDP: rx <- %d\r\n", rd);
00231                             iomutex.unlock();
00232                         }
00233 
00234                         if (rx_seq.cmp(buffer, rd) == 0) {
00235                             rx_seq.skip(rd);
00236                             rx_count += rd;
00237                             known_time = timer.read_ms();
00238                             if (window < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX) {
00239                                 window += MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN;
00240                             }
00241                         }
00242                     } else if (timer.read_ms() - known_time >
00243                             MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_TIMEOUT) {
00244                         // Dropped packet or out of order, revert to last good sequence
00245                         // and cut buffer in half
00246                         tx_seq = rx_seq;
00247                         tx_count = rx_count;
00248                         known_time = timer.read_ms();
00249                         if (window > MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) {
00250                             window /= 2;
00251                         }
00252 
00253                         if (MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_DEBUG) {
00254                             iomutex.lock();
00255                             printf("UDP: Dropped, window = %d\r\n", window);
00256                             iomutex.unlock();
00257                         }
00258                     } else if (rd == NSAPI_ERROR_WOULD_BLOCK ) {
00259                         break;
00260                     }
00261                 }
00262             }
00263 
00264             err = sock.close();
00265             TEST_ASSERT_EQUAL(0, err);
00266         }
00267     }
00268 };
00269 
00270 PressureTest *pressure_tests[MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS];
00271 
00272 
00273 void test_udp_packet_pressure_parallel() {
00274     uint8_t *buffer;
00275     size_t buffer_size;
00276     generate_buffer(&buffer, &buffer_size,
00277         MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN,
00278         MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX);
00279 
00280     size_t buffer_subsize = buffer_size / MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS;
00281     printf("MBED: Generated buffer %d\r\n", buffer_size);
00282     printf("MBED: Split into %d buffers %d\r\n",
00283             MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS,
00284             buffer_subsize);
00285 
00286     net = MBED_CONF_APP_OBJECT_CONSTRUCTION;
00287     int err =  MBED_CONF_APP_CONNECT_STATEMENT;
00288     TEST_ASSERT_EQUAL(0, err);
00289 
00290     printf("MBED: UDPClient IP address is '%s'\n", net->get_ip_address());
00291 
00292     udp_addr.set_ip_address(MBED_CONF_APP_ECHO_SERVER_ADDR);
00293     udp_addr.set_port(MBED_CONF_APP_ECHO_SERVER_PORT);
00294 
00295     timer.start();
00296 
00297     // Startup pressure tests in parallel
00298     for (int i = 0; i < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; i++) {
00299         pressure_tests[i] = new PressureTest(&buffer[i*buffer_subsize], buffer_subsize);
00300         pressure_tests[i]->start();
00301     }
00302 
00303     for (int i = 0; i < MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS; i++) {
00304         pressure_tests[i]->join();
00305         delete pressure_tests[i];
00306     }
00307 
00308     timer.stop();
00309     printf("MBED: Time taken: %fs\r\n", timer.read());
00310     printf("MBED: Speed: %.3fkb/s\r\n",
00311             MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_THREADS*
00312             8*(2*MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MAX -
00313             MBED_CFG_UDP_CLIENT_PACKET_PRESSURE_MIN) / (1000*timer.read()));
00314 
00315     net->disconnect();
00316 }
00317 
00318 
00319 // Test setup
00320 utest::v1::status_t test_setup(const size_t number_of_cases) {
00321     GREENTEA_SETUP(120, "udp_echo");
00322     return verbose_test_setup_handler(number_of_cases);
00323 }
00324 
00325 Case cases[] = {
00326     Case("UDP packet pressure parallel", test_udp_packet_pressure_parallel),
00327 };
00328 
00329 Specification specification(test_setup, cases);
00330 
00331 int main() {
00332     return !Harness::run(specification);
00333 }