Git Product home page Git Product logo

roscon-2023-realtime-workshop's Introduction

Real-time programming with ROS 2

This is the repository for the Real-time programming with ROS 2 workshop for ROScon 2023.

Thank you to Shuhao Wu, Jan Staschlat, Stephanie Eng and Oren Bell for putting together the excellent workshop. Slides available here (backup link). Thank you all for attending!

Workshop setup for laptops

To get the most out of the workshop, please complete the setup instructions for your laptop ahead of time. This will take about 10-20 minutes of your time and hopefully will make the workshop go significantly smoother. If you run into any problems, please file an issue in this repository.

System requirements

The workshop code only supports Linux. Officially, we only test Ubuntu 22.04 as the laptop/development operating system. However, with the Docker setup described below, it may be possible to run this on other Linux variants, although this is untested.

A real-time kernel is not necessary, although you may see decreased maximum latency numbers if you have a real-time kernel. For reference, the workshop code is developed on regular Ubuntu kernels.

Docker for Mac and Docker for Windows are not supported. Please try to bring a Linux laptop to the workshop for best results.. It is possible that a virtual machine running on Linux may work for you, although this is also not supported.

Podman is not supported. It might work, but we have not tested the Docker setup below with Podman.

The code is tested on both amd64 and arm64 (via the Raspberry Pi 4). No tests are made against the M1/M2 Macbooks.

Using Docker

The simplest way to run the workshop code is to use the workshop-specific Docker image. To get the Docker image, there are two options: (1) getting the image before the workshop (highly recommended), or (2) getting the image during the workshop (we cannot guarantee this experience will be good due to constraints of networking during a conference).

Before getting the image, you must install Docker Engine on your Linux machine. Please follow the Docker Engine installation instructions. Ensure you do not use Docker Desktop as that is not supported due to its use of a virtual machine. At the end, you should confirm that you see the following output when running the following commands:

$ docker version | grep -A2 Client
Client: Docker Engine - Community
 Version:           24.0.6
 API version:       1.43

$ docker version | grep -A2 Server
Server: Docker Engine - Community
 Engine:
  Version:          24.0.6

The exact version of Docker may be different than the above output.

You must also install Docker such that you can manage docker without root. See the instruction here for details.

Getting the image BEFORE the workshop

  1. Download the image file from the latest release. The file is called docker-image.tar.gz.
  2. Clone the roscon-2023-realtime-workshop repository with --recursive option: git clone --recursive https://github.com/ros-realtime/roscon-2023-realtime-workshop.git.
  3. cd into the roscon-2023-realtime-workshop repository.
  4. docker/fetch ~/Downloads/docker-image.tar.gz.

This should import the Docker image with a name of roscon-2023-realtime-workshop.

Note: do NOT use docker import or docker load. Please use the docker/fetch script above.

Starting the Docker container

If you would like to use VS Code and its dev container system, you can skip this section and go directly to Using Visual Studio Code.

After importing the Docker image, you can start the Docker container via the special shell file docker/start:

  1. cd into this repository.
  2. docker/start

This should start the Docker container and it will mount this repository into the /code directory inside the container. As a result, changes to the repository in the host will be reflected in the container.

This should start the Docker container with all of the appropriate privileges to run real-time-scheduled applications. It also setup the container with everything needed to run GUI programs.

Log into the Docker container

After starting the Docker container, you can login to the Docker container using the special docker/shell script:

  1. cd into this repository.
  2. docker/shell

You should be greeted with something like the following:

To run a command as administrator (user "root"), use "sudo <command>".
See "man sudo_root" for details.

user@6896a5d8dd84:/code$

This script logs you into an user named user which has the same UID and GID as your host user. This eliminates some of the permission errors you may encounter with using Docker. It also allows you to run GUI programs.

Checking everything works

After logging into the Docker container, you should check everything works:

Check the trace viewer tools are available

  1. After starting the Docker container, open a browser.
  2. Navigate to http://localhost:3100.
  3. Ensure a webpage like the following screenshot loads up.

Make sure all exercises compile and exercise 1 runs

After logging into the Docker container:

$ cd /code/exercise1 && colcon build
$ cd /code/exercise2-1 && colcon build
$ cd /code/exercise2-2 && colcon build
$ cd /code/exercise3-1 && colcon build
$ cd /code/exercise3-2 && ./build.sh
$ cd /code/exercise4-1 && colcon build
$ cd /code/exercise4-2 && colcon build

Make sure these all build. Then make sure at least exercise 1 runs (which takes 10 seconds):

$ cd /code/exercise1
$ install/latency_tester/bin/latency_tester
Testing latency for 10 seconds with 2 threads...
Latency testing complete. Trace file available at: exercise1.perfetto

You can load the exercise1.perfetto file in the trace viewer that you checked above and see some data being plotted.

Check rviz2 is working

After logging into the Docker container, run:

$ rviz2

The usual rviz2 GUI window should show up if all is well.

Using Visual Studio Code

If you like to use VS code for development, you must install the Remote Development Extension Pack, which enables the dev container system.

This repository contains a .devcontainer setup. This setup relies on the image imported above using the docker/fetch script. So if you haven't performed the image import step, launching VS code with dev containers will not work as it will fail to find the image.

Once you imported the image, you can simply open VS code with dev containers in this repository. You can use the VS code terminal or use the .devcontainer/shell script to login to the container. All above features should work and you should check.

Getting IntelliSense to work

To get IntelliSense to work with the C++ extension, you must select the appropriate build configuration for each exercise with the button on the bottom right side of your status bar, as indicated by the following screenshot:

You also have to build the code for that exercise at least once via colcon build. Sometimes, a Reload Window command is needed (CTRL+P -> Developer: Reload Window) to make IntelliSense work fully.

Workshop setup for the Raspberry Pi 4

We will have a limited number of Raspberry Pi 4s to loan to attendees. We expect people to form groups of 3-5 to work on problems together. That said, we welcome you to bring your own Raspberry Pi 4s as well. We recommend the following hardware:

  • A Raspberry Pi 4 (4GB+ is recommended, 2GB may be OK)
  • An Ethernet cable and any necessary USB Ethernet adapters to connect the Raspberry Pi directly to your laptop
  • A microSD card with greater than 8GB
  • The Raspberry Pi 4 power supply with USA power plugs

Before the workshop: flashing the image

A specially-crafted image designed for the workshop must be flashed to the SD card. This image contains an Ubuntu 22.04 installation with ROS 2 humble and a real-time kernel which is based on the Raspberry Pi 4 image maintained by the ROS 2 real-time working group. It has the code of the exercises builtin and has all of the dependencies installed, to ensure the experience can be had entirely offline. It also contains a number of helper utilities designed to make it easier for attendees for the workshop.

To download the image, go to the latest release. Download the roscon2023-rt-workshop-rpi4-ubuntu-22.04.1-ros2-humble.img.zst. This is a zstd-compressed file to save space. You should be able to double click it and extract the image file. Alternatively, you can use the command:

$ unzstd roscon2023-rt-workshop-rpi4-ubuntu-22.04.1-ros2-humble.img.zst

Locate a microSD card with greater than 16GB of storage and use Etcher to flash the resulting image file to your microSD card.

Directly flashing the image in one command

Alternatively, you can also use the command:

$ unzstd roscon2023-rt-workshop-rpi4-ubuntu-22.04.1-ros2-humble.img.zst --stdout | sudo dd of=/dev/sdX bs=16M conv=fdatasync oflag=direct

Please replace the /dev/sdX with the appropriate device.

Starting and connecting to the Raspberry Pi 4

Start the Raspberry Pi normally. You do not need to attach a monitor or a keyboard. To connect to the Raspberry Pi:

  1. Connect the Ethernet to the Raspberry Pi 4's only Ethernet port.
  2. Connect the other end of the Ethernet to your laptop. You may have to do this via an USB Ethernet adapter if you do not have an Ethernet port directly on your laptop.
  3. Wait briefly for the Raspberry Pi 4 to boot up and your laptop to connect to the network. This may take a few minutes on first boot.
  4. Once connected, the Raspberry Pi 4 is accessible at the IP address 192.168.10.1.
  5. You can ssh [email protected]. The password is ubuntu.
  6. The code for the exercises is located in the /code path.`

Run the pre-built exercise 1

The image contains not only the source code, but also pre-built binaries for all the exercises. This is to reduce the amount of time needed to build during the workshop. Please check this is working by running exercise 1:

  1. Log in to the Raspberry Pi using the instructions above.
  2. cd /code/exercise1
  3. ./run.sh

You may see warnings about loop overruns. This is expected as part of the exercise is to resolve this.

This program will generate a trace file located at /code/exercise1/exercise1.perfetto. You can download it via scp or use the browser as documented below.

Connect to the Raspberry Pi 4's builtin HTTP server to download files

The image contains a built-in HTTP server where you can browse the /code directory located on the Pi. This allows you to more easily download trace files, which we will do during the workshop. Please check this is working ahead of time:

  1. With Ethernet connected to the Raspberry Pi, go to http://192.168.10.1/repo/.
  2. Click into exercise1 and download exercise1.perfetto (this will only exists if you ran exercise 1 as instructed in the above section).
  3. Go to http://192.168.10.1/perfetto/
  4. On the top left of the screen, click Open trace file and open the exercise1.perfetto file you just downloaded.
  5. A timeline view should show up. You can press W on your keyboard to zoom in. Press ? to get help on how to use the interface if you would like to explore further.

roscon-2023-realtime-workshop's People

Contributors

janstaschulat avatar nightduck avatar shuhaowu avatar stephanie-eng avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

roscon-2023-realtime-workshop's Issues

Investigate dedicated WiFi setup for the Raspberry Pi 4

  • What model of access point can I buy that can support ~150 devices (assume 25 RPI4 + 100 laptops connected at the same time)
  • See if we can have anything host some code/files so we can download them to laptops during the workshop without internet access

Docker installation problem

Hi, here's what i see when I run the commands recommended


chief-of-mischief@gaurang-legion:~/Desktop$ docker version | grep -A2 Client
Client: Docker Engine - Community
Cannot connect to the Docker daemon at unix:///home/chief-of-mischief/.docker/desktop/docker.sock. Is the docker daemon running?
 Version:           24.0.6
 API version:       1.43
chief-of-mischief@gaurang-legion:~/Desktop$ systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset>
     Active: active (running) since Mon 2023-10-16 14:10:14 EDT; 2min 6s ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 7574 (dockerd)
      Tasks: 29
     Memory: 34.7M
        CPU: 614ms
     CGroup: /system.slice/docker.service
             └─7574 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/cont>

Oct 16 14:10:14 gaurang-legion systemd[1]: Starting Docker Application Containe>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.2729658>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.2733908>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.3874492>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.7583824>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.7982099>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.7983349>
Oct 16 14:10:14 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:14.8803635>
Oct 16 14:10:14 gaurang-legion systemd[1]: Started Docker Application Container>
Oct 16 14:10:22 gaurang-legion dockerd[7574]: time="2023-10-16T14:10:22.6777447>
chief-of-mischief@gaurang-legion:~/Desktop$ sudo usermod -aG docker $USER
chief-of-mischief@gaurang-legion:~/Desktop$ docker version | grep -A2 Client
Cannot connect to the Docker daemon at unix:///home/chief-of-mischief/.docker/desktop/docker.sock. Is the docker daemon running?
Client: Docker Engine - Community
 Version:           24.0.6
 API version:       1.43
chief-of-mischief@gaurang-legion:~/Desktop$ ls -l /var/run/docker.sock
srw-rw---- 1 root docker 0 Oct 16 14:10 /var/run/docker.sock
chief-of-mischief@gaurang-legion:~/Desktop$ 

Is this okay or should I make changes?

setup issue

After installing docker, I need to prefix all docker commands with sudo

without sudo,

david@home:~/roscon-2023-realtime-workshop$ docker/fetch ~/Downloads/docker-image.tar.gz 
Trying to import /home/david/Downloads/docker-image.tar.gz to roscon-2023-realtime-workshop. This should take about a minute.
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.24/images/load?quiet=0": dial unix /var/run/docker.sock: connect: permission denied

With sudo docker/fetch ~/Downloads/docker-image.tar.gz it seemed to succeed

david@home:~/roscon-2023-realtime-workshop$ sudo docker/fetch ~/Downloads/docker-image.tar.gz
Trying to import /home/david/Downloads/docker-image.tar.gz to roscon-2023-realtime-workshop. This should take about a minute.
Loaded image: roscon-2023-realtime-workshop:latest
Image has been imported. Try running the following command to start the container:

  docker/start

But when I tried to run
sudo docker/start from roscon repo folder, and get the following message

david@home:~/roscon-2023-realtime-workshop$ sudo docker/start
+ docker stop roscon-2023-realtime-workshop
Error response from daemon: No such container: roscon-2023-realtime-workshop
+ docker rm -f roscon-2023-realtime-workshop
Error response from daemon: No such container: roscon-2023-realtime-workshop
+ docker container inspect roscon-2023-realtime-workshop
++ pwd
++ id -u
++ id -g
+ docker run -d --device=/dev/dri --volume=/tmp/.X11-unix:/tmp/.X11-unix --volume=/home/david/roscon-2023-realtime-workshop:/code --ulimit=rtprio=98 --ulimit=memlock=-1 --cap-add=SYS_NICE --init --rm -p 3100:3100 -e DISPLAY=:0 -e QT_X11_NO_MITSHM=1 -e HOST_UID=0 -e HOST_GID=0 --name=roscon-2023-realtime-workshop roscon-2023-realtime-workshop
d746b71e0686ac35ba28926c95860b40ab16849954b25607c7fc3892fe2ed366
+ set +x
Started container roscon-2023-realtime-workshop. To enter the container, run the command:

  docker/shell
david@home:~/roscon-2023-realtime-workshop$ sudo docker/shell
Error response from daemon: No such container: roscon-2023-realtime-workshop

image doesn't include dependencies for exercise3-2

Until @shuhaowu releases the new docker and raspberry pi image, the workaround is:

# Start up docker container
cd exercise3-2
sudo rosdep init
rosdep update
sudo apt update
rosdep install --from-paths src --ignore-src

Then exercise3-2 will build

Create solutions folder

Remove snippets of code from 3-1, 3-2, 4-1, and 4-2, and put the complete code in a solutions folder.

Impartial code should still build without errors

docker/start fails when image is already in use

This may only impact us developers who have to launch it more than once, but there is a persistent issue where calling stop on the previous container also removes it but does so asynchronously. This causes conflicts when attempting to restart it, but the user is still prompted to run docker/shell (which will fail, because there's no container running).

oren@oren-mc1040:~/git/roscon-2023-realtime-workshop$ docker/start
+ docker stop roscon-2023-realtime-workshop
roscon-2023-realtime-workshop
+ docker rm roscon-2023-realtime-workshop
Error response from daemon: removal of container roscon-2023-realtime-workshop is already in progress
++ pwd
++ id -u
++ id -g
+ docker run -d --device=/dev/dri --volume=/tmp/.X11-unix:/tmp/.X11-unix --volume=/home/oren/git/roscon-2023-realtime-workshop:/code --ulimit=rtprio=98 --ulimit=memlock=-1 --cap-add=SYS_NICE --init --rm -p 3100:3100 -e DISPLAY= -e QT_X11_NO_MITSHM=1 -e HOST_UID=1000 -e HOST_GID=1000 --name=roscon-2023-realtime-workshop roscon-2023-realtime-workshop
docker: Error response from daemon: Conflict. The container name "/roscon-2023-realtime-workshop" is already in use by container "77bbd844afd149378336d2d5e3173f1ec3f066fb9aee0159993098d5e3d20e54". You have to remove (or rename) that container to be able to reuse that name.
See 'docker run --help'.
+ set +x
Started container roscon-2023-realtime-workshop. To enter the container, run the command:

  docker/shell

exercise 3-2: missing packages

when building exercise 3-2, the following packages were missing (dependency of rclcpp)

 --- stderr: performance_test_fixture                                           
CMake Error at CMakeLists.txt:20 (find_package):
  By not providing "Findbenchmark.cmake" in CMAKE_MODULE_PATH this project
  has asked CMake to find a package configuration file provided by
  "benchmark", but CMake did not find one.

  Could not find a package configuration file provided by "benchmark" with
  any of the following names:

    benchmarkConfig.cmake
    benchmark-config.cmake

  Add the installation prefix of "benchmark" to CMAKE_PREFIX_PATH or set
  "benchmark_DIR" to a directory containing one of the above files.  If
  "benchmark" provides a separate development package or SDK, be sure it has
  been installed.
---
Failed   <<< performance_test_fixture [1.08s, exited with code 1]

https://github.com/ros2/performance_test_fixture/blob/humble/CMakeLists.txt

Error message on docker/start, and cannot open Perfetto

Hi yall

I'm on a Ubuntu 22.04 host. Not using VS Code / dev containers.

When I get to docker/start, I get an error message, but it seems to exit normally (see line 2):

$ docker/start
roscon-2023-realtime-workshop
Error response from daemon: No such container: roscon-2023-realtime-workshop
++ pwd
++ id -u
++ id -g
+ docker run -d --device=/dev/dri --volume=/tmp/.X11-unix:/tmp/.X11-unix --volume=/home/morten/workspace/roscon-2023-realtime-workshop:/code --ulimit=rtprio=98 --ulimit=memlock=-1 --cap-add=SYS_NICE --init --rm -p 3100:3100 -e DISPLAY=:0.0 -e QT_X11_NO_MITSHM=1 -e HOST_UID=1001 -e HOST_GID=1001 --name=roscon-2023-realtime-workshop roscon-2023-realtime-workshop
20c5f8de0c5479caa10f811502a21daef74cda6e674caad4fc74031d568e40b9
+ set +x
Started container roscon-2023-realtime-workshop. To enter the container, run the command:

  docker/shell

I'm then able to run docker/shell and I see the expected prompt. However, opening http://localhost:3100/ doesn't work (won't load).

Test new docker image setup instructions

The newest docker image is uploaded to Github. There's also updated READMEs that details how to use this. Please follow the instructions and let me know if there's anything wrong with it, and if anything doesn't work for you.

Please also test the VS code setup if you use vscode (otherwise feel free to skip). Some people might be tempted to use it and it should just work.

cc: @nightduck @JanStaschulat @stephanie-eng

Design Excercise 4

4-1 high priority node ( real-time threads, multiple Executors)

Goal:

  • Learn, how to prioritize entire ROS node
  • real-time behavior for a timer/subscription, which are in the same ROS node

User Experience:

  • setup A: baseline : plain vanilla executor, timer/subscription is delayed
  • setup B: with prioritization: plain vanilla executor, timer/subscription is never delayed

Visualization:

  • setup A: user views tracing and can sees delays
  • setup B: user views tracing output and does not see any delays

exercise for user:

  • baseline (provided)
  • execute setup A
  • publisher is already with real-time priority
  • user does the same thing for sensor_node
  • execute setup B
  • compare tracing results (save previous logout, rename)

4-2 processing chain (callback-groups, real-time threads, multiple Executors)

Goal:

  • Learn, how to prioritize multiple callbacks distributed over multiple nodes within one process.
  • mixed-criticality use-case, real-time and non real-time is distributed over multiple nodes.

User Experience:

  • setup A: baseline : plain vanilla executor, subscription is delayed, high jitter on end-to-end latency
  • setup B: with prioritization: plain vanilla executor, timer/subscription on high priority path not delayed, low jitter on end-to-end latency

Visualization:

  • setup A: user views tracing and can sees high jitter on end-to-end latency of high-priority path
  • setup B: user views tracing output and observes low jitter on end-to-end latency of high-priority path

exercise for user:

  • baseline (provided)
  • execute setup A and visualize trace (observe high jitter)
  • publisher_node has setup with callback_group/real-time thread
  • user applies this to sensor_node/ObjectDetector subscription and actuation_node/subscription
  • execute setup B
  • compare tracing results (save previous logout, rename)

Design Excercises 3

application setup:

system.cpp:

  • publisher_node
  • actuation_node

sensor.cpp:

  • one node with

    • object detector subscription
    • data logger subscription
  • main.cpp
    MultiThreadedExecutor exe;
    exe.add_node(publisher_node)
    exe.add_node(actuation_node)
    exe.add_node(sensor)

demonstrate limitations of vanilla ROS Executor

option A:

  • use SingleThreadedExecutor

option B:

  • use MultiThreadedExecutor

demonstration with tracer:

pub1     p        p
pub2     p        p

          |        |
sub1      xx       xxxxxx     

          |        |
sub2      ..ooo    ......ooo

delay:    short     long

simplification:

  • not fully loaded system, short execution time of publisher =>
    timestamp assigned in publisher
    (almost equal to) =
    timestamp when message is received by subscriptions
  • user experience: sometimes sub2 callback gets delayed (by callback sub1)

visualization of End-To-End Latency:

  • measure end-to-end latencies (publisher -> object detector -> actuation)
  • user expericence: large variation

real-time-executor

sub2 is real-time subscription

visualization:

pub1     p        p
pub2     p        p

          |        |
sub1      ...xx    ...xxxxxx     

          |        |
sub2      ooo      ooo

delay:    short     short

End-To-End-Latency

  • user experience: always short latency (small variation)

Oct 6 todo

  • Exercise 4 preparation for testing (readme and slides), review session
  • Exercise 2 preparation for testing (readme and slides), review session
  • Writing emails to panelists and attendees
  • Minor adjustment to timing on website
  • Ahead of time Raspberry Pi setup instructions
  • Schedule Exercise 1 and Exercise 3 testing time (Sunday)
  • Publisher executor rename
  • Run script for exercise 2 should be uniform

Exercise 2 README improvements

  • - The wording "Replace the double with a std::atomic<double>, and remove the locks": needs to point user to the file path.
  • - File paths should be absolute.
  • - static_assert(std::atomic<double>::is_always_lock_free); is already there in single_data.h in exercise 2-1. Consider in the text, putting the sentence closer together. Consider telling user to add the static assert line immediately above the atomic.
  • - Consider making the "remove the lock" sentence more visible.
  • - After exercise 2-1 is complete, tell the user what to expect.
  • - Consider telling user to load their own exercise2-1.perfetto file.
  • - Don't use stress-ng and use the single stress.sh file.
  • - What do I do at the end of exercise 2-1? I have the file and what am I suppposed to look for?
  • - After seeing the results of 2-1, consider telling people to switch to nanosecond view if they see a single column at 1us.
  • - run-exercise2-2.sh command needs to be ./
  • - Consider putting latency view in focus over timeline view
  • - In exercise 2-2: tell users (stress in bold) to not remove the lock after replace with atomic, and simply compile first to see if the static_assert succeeds.
  • - "This tends to be the case for larger amounts of data." -> This is almost always the case for data greater than 64/128bits depending on CPU architectures
  • - "A priority inheritance mutex implementation is available as a cactus_rt::mutex. Change the std::mutex to a cactus_rt::mutex." -> Consider saying remove the atomic and static assert before this step.
  • - " periodically pops all data from the data queue at 100 Hz". Consider referring to PopData()
  • - "unction is meant to simulate the ROS thread acquiring the lock and needing to do some work. " -> maybe something about simulating contention, as it's not that realistic to do processing while holding a lock
  • - Exercise 2-3 which slice do I visualize
  • - When letting people know which slice to visualize, mention the thread name too, and possibly als the time scale.
  • - Exercise 2-3 baseline: what should I expect as the audience?
  • Repeat yourself with the path in 2-3
  • 2-3: queue -> moodycamel::ReaderWriterQueue instead of omitting the type

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.