<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: onesignaldevs</title>
    <description>The latest articles on Forem by onesignaldevs (@onesignaldevs).</description>
    <link>https://forem.com/onesignaldevs</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F617001%2Fefc2aafd-5ce0-4b0c-b56a-e36601f77ae6.png</url>
      <title>Forem: onesignaldevs</title>
      <link>https://forem.com/onesignaldevs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/onesignaldevs"/>
    <language>en</language>
    <item>
      <title>Fixing Memory Leaks in Rust</title>
      <dc:creator>onesignaldevs</dc:creator>
      <pubDate>Mon, 23 May 2022 23:35:29 +0000</pubDate>
      <link>https://forem.com/onesignal/fixing-memory-leaks-in-rust-38l</link>
      <guid>https://forem.com/onesignal/fixing-memory-leaks-in-rust-38l</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W9vOCkyX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2022/05/fixing-memory-leaks-in-rust.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W9vOCkyX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2022/05/fixing-memory-leaks-in-rust.jpeg" alt="Fixing Memory Leaks in Rust" width="880" height="588"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At OneSignal, we love Rust. We have blogged before about &lt;a href="https://onesignal.com/blog/rust-at-onesignal/"&gt;pivoting some of our core business systems to Rust&lt;/a&gt;, &lt;a href="https://onesignal.com/blog/4-years-of-rust-at-onesignal/"&gt;how the language has changed over the past several years&lt;/a&gt;, and interesting things we’ve learned about the &lt;a href="https://onesignal.com/blog/thread-safety-rust/"&gt;thread safety model&lt;/a&gt;. Other than Onepush, the core of our push notification delivery system, we also use Rust for a gRPC service and multiple Kafka consumers. In this article, we’re going to talk about a stumbling block that we ran into with one of our newest Rust projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Near the end of February, we announced the general availability of &lt;a href="https://onesignal.com/blog/consistently-drive-value-with-journeys/"&gt;our Journeys feature&lt;/a&gt;. Journeys allows customers to easily build complex messaging workflows with a no-code UI. It is powered by a few gRPC services and a Kafka consumer written in Rust, called JourneyX. Journeys is an event-driven system, so everything that happens within a journey workflow is triggered by an event on a Kafka stream. These events are consumed by JourneyX (journey executor).&lt;/p&gt;

&lt;p&gt;A few weeks ago, as Journeys adoption started to increase and JourneyX started to process more events, we began to notice a disturbing pattern in its memory usage. The most active processes were consistently utilizing lots of memory, then getting killed by the kernel. The Linux kernel has a piece of functionality called the OOM (out-of-memory) killer, which will automatically kill processes when they consume too much of the system memory. This prevents the system from becoming unstable or locking up due to resource starvation. In our case, the JourneyX processes were constantly being killed, restarted, and killed again by the OOM killer. This showed up on a graph as a sawtooth pattern of rapid allocation and near-instant deallocation when the process was killed. Memory usage would spike up to 17 GiB in the busiest processes, but we expect one of these worker processes to need under 1 GiB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N2MS3CQ1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1650487816016_Screen%2BShot%2B2022-04-20%2Bat%2B13.50.01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N2MS3CQ1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1650487816016_Screen%2BShot%2B2022-04-20%2Bat%2B13.50.01.png" alt="Fixing Memory Leaks in Rust" width="880" height="370"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Graph of memory usage for a single JourneyX process over 24 hours&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This caused a few issues for us. Obviously from a theoretical perspective, we didn’t want a system that was constantly being killed by the OS. The operations it performed were idempotent, so we weren’t necessarily worried about sending multiple notifications due to this problem. The issue did create alert spam for us, which led us to over-provision on memory. The constant crashing also caused concern about the long-term health of the service.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;BUT WAIT!&lt;/em&gt; I hear you say — doesn’t Rust’s borrow checker prevent memory errors? Isn’t Rust supposed to be “safe?” It turns out that, according to Rust’s rules, leaking memory is completely safe! In fact, we can purposely leak as much memory as we want using the function &lt;code&gt;std::mem::forget&lt;/code&gt;. The only thing “unsafe” about memory leaks is that they might eventually result in your program being killed by the kernel (as JourneyX does in this case). A program ending in a predictable way is also considered safe behavior. Rust’s safety guarantees exist to protect us from invalid memory access, not resource starvation.&lt;/p&gt;
&lt;h2&gt;
  
  
  Troubleshooting with Distributed Tracing
&lt;/h2&gt;

&lt;p&gt;In addition to Rust, we’re big fans of distributed tracing. If you’re not familiar with tracing concepts, check out this document from Honeycomb's &lt;a href="https://www.honeycomb.io/blog/an-introduction-to-distributed-tracing/"&gt;Introduction to Distributed Tracing&lt;/a&gt;. All of our services report trace data and send &lt;a href="https://www.w3.org/TR/trace-context/"&gt;W3C trace propagation headers&lt;/a&gt; to one another so that we can tie operations together through our system. Tracing data in Honeycomb is now our first stop when our on-call team gets paged. Here is an example trace from our Journeys processing system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t_bzTa18--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1651174008475_Screen%2BShot%2B2022-04-28%2Bat%2B12.26.40.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t_bzTa18--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1651174008475_Screen%2BShot%2B2022-04-28%2Bat%2B12.26.40.png" alt="Fixing Memory Leaks in Rust" width="880" height="426"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In some languages, tracing instrumentation can be accomplished largely through automated means. In our Ruby on Rails codebase, the Honeycomb and OpenTelemetry libraries use ActiveSupport hooks to add trace spans around operations like HTTP handlers and ActiveRecord queries. Rust is a lot of things, but it's not really “runtime configurable.” There are libraries that allow you to write Rust code that can be configured at runtime, but most Rust code is not written in this way. Because of this, we need to add a lot of manual instrumentation to our Rust apps. We need to manually delineate operations into spans and add fields on those spans.&lt;/p&gt;

&lt;p&gt;We have a few choices for adding these data to our codebases. We could use the Rust &lt;code&gt;opentelemetry&lt;/code&gt; library, which is generic and standardized according to the OpenTelemetry specification. Instead, we use the tracing library, which is a façade crate, similar to &lt;code&gt;log&lt;/code&gt;. It allows us to connect and configure multiple tracing backends, and it captures data from the &lt;code&gt;log&lt;/code&gt; crate straight out of the box.&lt;/p&gt;

&lt;p&gt;If we had some Rust code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;http_handler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;perform_expensive_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you wanted to put a span around the &lt;code&gt;perform_expensive_computation&lt;/code&gt; function, you could do so like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;info_span&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;http_handler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;info_span!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"perform_expensive_computation"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;perform_expensive_computation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;enter&lt;/code&gt; function returns a guard that marks the time in which the span is active. It begins when you first call &lt;code&gt;enter&lt;/code&gt;, and ends when the guard value is dropped as it goes out of scope. Rust’s &lt;code&gt;Drop&lt;/code&gt; trait allows it to easily provide the functionality to run at the time it’s deallocated.&lt;/p&gt;

&lt;p&gt;Within JourneyX, surrounding the “send a notification” HTTP request, we had the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;query_span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;info_span!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"send HTTP request"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.client&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;.into_parts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now experienced users of the tracing library may already know the issue, but to those of us who aren’t intimately familiar with the API, this looks quite reasonable based on what I’ve already told you. If we look at the documentation for the enter method though, we may notice something that calls this into question.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning&lt;/strong&gt; : in asynchronous code that uses &lt;a href="https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html"&gt;async/await syntax&lt;/a&gt;, Span::enter should be used very carefully or avoided entirely. Holding the drop guard returned by Span::enter across .await points will result in incorrect traces.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Notice the last line of the block above uses &lt;code&gt;.await&lt;/code&gt; when calling the &lt;code&gt;request&lt;/code&gt; method. This is going to cause a problem for us. To see why, we need to look at the conceptual models for &lt;code&gt;tracing&lt;/code&gt; and async Rust.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;.enter&lt;/code&gt; method from &lt;code&gt;tracing&lt;/code&gt; returns what is conventionally referred to as a “guard” type. One of the great benefits of Rust’s ownership and lifetime system is that it provides the ability to write destructors for any type and to be sure about when those destructors are going to run. We can do this by implementing the Drop trait for any type. The interface looks (very, very roughly) like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;EnterGuard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Span&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;EnterGuard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;begin_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;EnterGuard&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Drop&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;EnterSpan&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;drop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;end_span&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because of Rust’s destructor rules, the &lt;code&gt;EnterSpan&lt;/code&gt; type will call the &lt;code&gt;end_span&lt;/code&gt; function whenever the variable it’s attached to goes out of scope. Here is an annotated example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// begin_span called&lt;/span&gt;
    &lt;span class="nf"&gt;do_expensive_computation&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// end_span called by the Drop of `_guard`&lt;/span&gt;

  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// begin_span called&lt;/span&gt;
  &lt;span class="nf"&gt;more_compute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// end_span called by the Drop of `_guard`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is great because it means that most of the time, we just don’t need to worry about remembering to mark a span as complete. It just works (tm). For synchronous code, that’s definitely the case, but it falls apart for async code. To see why that's the case, let’s look at the pseudocode for &lt;code&gt;begin_span&lt;/code&gt; and &lt;code&gt;end_span&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;thread_local!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;ACTIVE_SPANS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;RefCell&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SpanId&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;RefCell&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;begin_span&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SpanId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ACTIVE_SPANS&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;end_span&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;ACTIVE_SPANS&lt;/span&gt;&lt;span class="nf"&gt;.with&lt;/span&gt;&lt;span class="p"&gt;(|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="nf"&gt;.borrow_mut&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now this is incredibly simplified (please don’t get mad at me, tracing maintainers) but the general idea is that each thread maintains a stack of span IDs that represents the hierarchy of spans currently active. This works great for synchronous code, when each OS thread can maintain this state independently. When we use async code, however, we have multiple tasks running concurrently on top of the same OS thread, sharing thread locals. And the &lt;code&gt;Drop&lt;/code&gt; handler for our &lt;code&gt;EnterGuard&lt;/code&gt; won’t be called when context switches happen. That means if we were to write code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;FuturesUnordered&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;info_span!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"task"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_millis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nn"&gt;futures&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;future&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;join_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s consider what happens to our thread-local stack at each stage in the task execution. For simplicity's sake, we’ll assume for now that these five tasks are executed in the order that they were enqueued, although this typically isn't the case.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--p3WhJVXP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1651519448041_tracing-real-path.drawio.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--p3WhJVXP--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1651519448041_tracing-real-path.drawio.png" alt="Fixing Memory Leaks in Rust" width="880" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that at both the beginning and end of the spans there are large discrepancies in the state of the stack. When spans are beginning, we create a hierarchy on the stack that indicates all of the tasks have a parent/child relationship with one another, but this is not accurate. When spans end, we pop the wrong task span off of the stack, because the state of the stack is inconsistent with reality.&lt;/p&gt;

&lt;p&gt;In our production code, this issue caused memory usage to skyrocket, because our span tree was essentially expanding endlessly and never able to shrink. Because of how quickly concurrent operations were occurring, the stack was continuously having new spans added to it, and previous spans (now with tens of thousands of children) would never be dropped.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;So what’s the fix? It’s very simple. Recall that the original code looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;query_span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;info_span!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"send HTTP request"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_guard&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;query_span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.client&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;.into_parts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to update it to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;query_span&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tracing&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;info_span!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"send HTTP request"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.client&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_span&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nf"&gt;.into_parts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a subtle change, but absolutely critical. Let’s see how this works. This is once again simplified for the sake of brevity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Instrument&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Instrumented&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Instrumented&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Instrumented&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Span&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Instrumented&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;T&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Poll&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_enter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.span&lt;/span&gt;&lt;span class="nf"&gt;.enter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.inner&lt;/span&gt;&lt;span class="nf"&gt;.poll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few things going on here. First off, we have the &lt;code&gt;Instrument&lt;/code&gt; trait. This is an extension trait that exists only to allow us to build an &lt;code&gt;Instrumented&lt;/code&gt; struct from an arbitrary &lt;code&gt;Future&lt;/code&gt;. &lt;code&gt;Instrumented&lt;/code&gt; is a wrapper future that allows us to have tight control over the polling logic. In this case, we use &lt;code&gt;.enter&lt;/code&gt; to begin the span while the underlying future is polled, and end the span when the context switches.&lt;/p&gt;

&lt;p&gt;This works because &lt;code&gt;poll&lt;/code&gt; is the context-switching boundary of a future. While &lt;code&gt;poll&lt;/code&gt; is being called, this future and its children are the only things burning CPU time on the thread. Other futures may be sleeping in the background, but nothing else can preempt the currently running future’s &lt;code&gt;poll&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tracing’s &lt;code&gt;.enter&lt;/code&gt; method isn’t the only instance of this. There are many types that were not designed to be held across await points. &lt;code&gt;Mutex&lt;/code&gt;, &lt;code&gt;RwLock&lt;/code&gt;, and &lt;code&gt;RefCell&lt;/code&gt; for example all have guard types that hold a lock on a resource and could result in deadlocks if held across await points.&lt;/p&gt;

&lt;p&gt;So where do we go from here? There’s a subtle code difference that results in both incorrect observability results and potentially extreme memory usage. Normally, issues like this could be caught by the Rust compiler. There is an RFC to add a lint to the compiler called &lt;a href="https://github.com/rust-lang/rust/issues/83310"&gt;&lt;code&gt;must_not_suspend&lt;/code&gt;&lt;/a&gt; which will allow developers to mark types as being unsafe to hold across await points. But there is still a lot of work remaining before this will land, so what should we do in the meantime?&lt;/p&gt;

&lt;p&gt;Rust’s standard linting tool, Clippy, has existing lints for the &lt;code&gt;Mutex&lt;/code&gt; and &lt;code&gt;RefCell&lt;/code&gt; types, but we don’t want to have to create a custom Clippy lint for every type like this. A few weeks ago, I submitted &lt;a href="https://github.com/rust-lang/rust-clippy/pull/8707"&gt;rust-lang/clippy#8707&lt;/a&gt; which allows Clippy users to provide a list of types that are not allowed to be held across await points. Once this makes it to Clippy’s stable release branch, users will be able to add the following to their &lt;code&gt;clippy.toml&lt;/code&gt; files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;holding&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;invalid&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"tracing::trace::Entered"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"tracing::trace::EnteredSpan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will provide users with a warning if a span’s enter guard is held across an await point when they run cargo Clippy. The warnings will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: `tracing::trace::Entered` may not be held across an `await` point per `clippy.toml`
  --&amp;gt; main.rs:10:9
   |
LL | let _guard = span.enter();
   | ^^^^^^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This provides some amount of safeguarding, though the &lt;code&gt;must_not_suspend&lt;/code&gt; lint will be a welcome improvement, as it will not require an explicit configuration in order to work.&lt;/p&gt;

&lt;p&gt;Thank you for coming with me on this journey of memory leak discovery! I’ll leave you with a four-day graph of the memory usage of our JourneyX processes. It’s quite clear when this change was deployed that we instantly fixed the issue with this very subtle code change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xm_-yHow--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1650305781135_Screen%2BShot%2B2022-04-18%2Bat%2B10.03.19.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xm_-yHow--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_A27BFDF87D2127DCD6BB2EBAD04C599513C407E65763082467920C2770D2212C_1650305781135_Screen%2BShot%2B2022-04-18%2Bat%2B10.03.19.png" alt="Fixing Memory Leaks in Rust" width="880" height="363"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A graph of the memory usage of our JourneyX processes over four days.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you’re interested in working on using Rust in production to create high-throughput evented systems, distributed systems, or any other areas of engineering, please check out the OneSignal careers page!&lt;/p&gt;

&lt;h3&gt;
  
  
  &amp;gt;&amp;gt; &lt;a href="https://onesignal.com/careers"&gt;Browse OneSignal Careers&lt;/a&gt;
&lt;/h3&gt;

</description>
      <category>rust</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Add Push Notifications to a Flutter App</title>
      <dc:creator>onesignaldevs</dc:creator>
      <pubDate>Mon, 14 Mar 2022 18:35:30 +0000</pubDate>
      <link>https://forem.com/onesignal/how-to-add-push-notifications-to-a-flutter-app-1jag</link>
      <guid>https://forem.com/onesignal/how-to-add-push-notifications-to-a-flutter-app-1jag</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XYBl0Z09--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2022/03/how-to-add-push-notifications-to-a-flutter-app.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XYBl0Z09--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2022/03/how-to-add-push-notifications-to-a-flutter-app.jpg" alt="How to Add Push Notifications to a Flutter App" width="880" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Push notifications have proved to be one of the most effective ways to enhance your user experience, improve app engagement, and boost retention. In this tutorial, we’ll show you how to integrate &lt;a href="https://onesignal.com/mobile"&gt;push notifications&lt;/a&gt; in your iOS and Android apps with minor configuration changes using the &lt;a href="https://documentation.onesignal.com/docs/flutter-sdk-setup"&gt;OneSignal Flutter SDK&lt;/a&gt;. In no time, you'll be able to send out transactional and promotional push notifications to users for free!&lt;/p&gt;

&lt;p&gt;If you are a Flutter developer and want to leverage push notifications to engage with your users, follow along to add this amazing functionality to your apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Guide Overview:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 1: Set Up Your OneSignal Account&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Part 2: Creating a Flutter App&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1: Set Up Your OneSignal Account
&lt;/h2&gt;

&lt;p&gt;To begin, &lt;a href="https://app.onesignal.com/login"&gt;log in&lt;/a&gt; to your OneSignal account or &lt;a href="https://app.onesignal.com/signup"&gt;create a free account&lt;/a&gt;. Then, click on the blue button entitled _ &lt;strong&gt;New App/Website&lt;/strong&gt; _ to configure your OneSignal account to fit your app or website.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cccs2Dzj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/SuayEKKWaLijFKYhGF4ijppOJqfprFUswRNI6DTWaLimqGtYmG55d-yYIw2DmIog9_X674_h3c7NW0ITCET5rdGBnzC1eZ8ycxsbkzqFMry8LPLSFNqg-eDguOkaf4PArmRe2FIC" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cccs2Dzj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/SuayEKKWaLijFKYhGF4ijppOJqfprFUswRNI6DTWaLimqGtYmG55d-yYIw2DmIog9_X674_h3c7NW0ITCET5rdGBnzC1eZ8ycxsbkzqFMry8LPLSFNqg-eDguOkaf4PArmRe2FIC" alt="How to Add Push Notifications to a Flutter App" width="880" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add your app name, select _ &lt;strong&gt;Google Android&lt;/strong&gt; _ if the target is Android devices or select _ &lt;strong&gt;Apple iOS&lt;/strong&gt; _ if the target is iOS devices. Then click, &lt;strong&gt;&lt;em&gt;Next: Configure Your Platform&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oxUzrzho--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/W9bhSNrbKFrkTMfVcxScu5x2GzRVj9_T7cVnkQKtlHU_V5_BFkHRsV2jkXaJD9BfulmUSVhYK26xDfEzXPqy2mw4RKBFE99vd87FVHBnrjf-CFGiDXWhN1-ymH-2-PzNFCx58rUK" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oxUzrzho--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/W9bhSNrbKFrkTMfVcxScu5x2GzRVj9_T7cVnkQKtlHU_V5_BFkHRsV2jkXaJD9BfulmUSVhYK26xDfEzXPqy2mw4RKBFE99vd87FVHBnrjf-CFGiDXWhN1-ymH-2-PzNFCx58rUK" alt="How to Add Push Notifications to a Flutter App" width="880" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For iOS, you will need to &lt;strong&gt;add an iOS push certificate&lt;/strong&gt;. If you're not sure about this step, read OneSignal's &lt;a href="https://documentation.onesignal.com/docs/generate-an-ios-push-certificate"&gt;instructions on how to generate an iOS push certificate&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4ZA8Z_Hu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/SS9EXmNZn6BPiaVQZaDUvNHWVH8Ba9IaoVKWlu0B1Wzc-52TK5IoJbhsomskMQ1tjsa2yBpFAMtgr72VB3AppQuI-LWhrccmNL7958EhKBZc4gs_Ee-N5WItgFxLxdbcTabp6P1m" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4ZA8Z_Hu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/SS9EXmNZn6BPiaVQZaDUvNHWVH8Ba9IaoVKWlu0B1Wzc-52TK5IoJbhsomskMQ1tjsa2yBpFAMtgr72VB3AppQuI-LWhrccmNL7958EhKBZc4gs_Ee-N5WItgFxLxdbcTabp6P1m" alt="How to Add Push Notifications to a Flutter App" width="880" height="316"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Apple iOS (APNs) Configuration in OneSignal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For Android, you will need to enter &lt;strong&gt;Firebase Server Key&lt;/strong&gt; and &lt;strong&gt;Server ID&lt;/strong&gt; that you can get by creating a new project on Firebase Console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B27kDBTc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/QSh1iDrnoIL5yuThK1hKjbHU67sxkuVLC4WtTjwLQ3TDnTE1sM6rsYtso3GmUo7VtbZ98o5byyRYlLm5M3OVW-Cynb4TeIfz4GfWlkUFtS2o-hv4szJ7GjKFmDAsH5A5NDJ7m_Fo" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B27kDBTc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/QSh1iDrnoIL5yuThK1hKjbHU67sxkuVLC4WtTjwLQ3TDnTE1sM6rsYtso3GmUo7VtbZ98o5byyRYlLm5M3OVW-Cynb4TeIfz4GfWlkUFtS2o-hv4szJ7GjKFmDAsH5A5NDJ7m_Fo" alt="How to Add Push Notifications to a Flutter App" width="880" height="334"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Google Android (FCM) Configuration in OneSignal&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To find the Firebase service key, log in to the Firebase console and click on &lt;strong&gt;&lt;em&gt;Add Project&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--rZ9UhZ9v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/HCgbFkPibXItPV_rMXZ4I5UhZ_VMJdLvDTvLkSOwWNbTUSz3pcUzjkymdFuFWhRcCQZ1wlpFIwAKltrc4zr6GzwXaHTtHe3Wnqbh4mwzaetwkeM15Vtpe4fLmEpqO-Q0f8ECuw4I" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rZ9UhZ9v--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/HCgbFkPibXItPV_rMXZ4I5UhZ_VMJdLvDTvLkSOwWNbTUSz3pcUzjkymdFuFWhRcCQZ1wlpFIwAKltrc4zr6GzwXaHTtHe3Wnqbh4mwzaetwkeM15Vtpe4fLmEpqO-Q0f8ECuw4I" alt="How to Add Push Notifications to a Flutter App" width="880" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the name of your project and turn off &lt;strong&gt;&lt;em&gt;Enable Google Analytics for this project&lt;/em&gt;&lt;/strong&gt; if you want,  then click on &lt;strong&gt;&lt;em&gt;Create Project&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--leKp6AaL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/7NimhzKwTi82XolqEwokp2pL_wU7vf2glRxVp6ldi4KQhKVu5zhXyB4IMzBS6qTxuibP3IEijZYRzLoDj-yQZUjvXY88yQQlsXYejTdxP-olVQ91GqE-TqSEn_VDBv3wUIxIF3Fc" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--leKp6AaL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/7NimhzKwTi82XolqEwokp2pL_wU7vf2glRxVp6ldi4KQhKVu5zhXyB4IMzBS6qTxuibP3IEijZYRzLoDj-yQZUjvXY88yQQlsXYejTdxP-olVQ91GqE-TqSEn_VDBv3wUIxIF3Fc" alt="How to Add Push Notifications to a Flutter App" width="880" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WYkrTygJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/NOI1N2J8u3Bip_-PzMNTvO2gvH49U0q77YKtHvu3JNXxZUMELoTrsAgFQEC_CVIchdW8mYMs5e8iYKT7sDyUyoOqSJgbapCZhKsp7ReAK9BtKCBg6eK-HV1TkN5oYkGBdYLqhcpu" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WYkrTygJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/NOI1N2J8u3Bip_-PzMNTvO2gvH49U0q77YKtHvu3JNXxZUMELoTrsAgFQEC_CVIchdW8mYMs5e8iYKT7sDyUyoOqSJgbapCZhKsp7ReAK9BtKCBg6eK-HV1TkN5oYkGBdYLqhcpu" alt="How to Add Push Notifications to a Flutter App" width="880" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click _ &lt;strong&gt;Create project&lt;/strong&gt; _ to save the new entry, then click _ &lt;strong&gt;continue&lt;/strong&gt; _.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ldx8YZ3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/R3gzEUHXu6FLVzT4YA3yE2rq7t9FlJEwKeueprwAFRhXF-cT5MxaTD-H-gOoPzypzBAIqPbAnnSDTfrOmpXUfKm31_u-PjCMVS-tVnyDWMzf2yhLoGureWuAl029DYUETpRC6Wlz" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ldx8YZ3w--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/R3gzEUHXu6FLVzT4YA3yE2rq7t9FlJEwKeueprwAFRhXF-cT5MxaTD-H-gOoPzypzBAIqPbAnnSDTfrOmpXUfKm31_u-PjCMVS-tVnyDWMzf2yhLoGureWuAl029DYUETpRC6Wlz" alt="How to Add Push Notifications to a Flutter App" width="880" height="296"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll be directed to the project dashboard where you have to click on the Setting icon next to _ &lt;strong&gt;Project Overview&lt;/strong&gt; _ and click on _ &lt;strong&gt;Project Settings&lt;/strong&gt; _ from the menu that appears.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7igylNaH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/xgBbRkPvoy5yrtjZgPrT_RHDWm2TnlJiQsMu2eHMid8ZrDjwuAtbQVfrNmdov-ZgVQfdeHphFFpcVLV7vXDafHApaj6QS8_VKcaU0SgVUTPwBekHbRiOQg4_KOIrhF1knue18zB5" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7igylNaH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/xgBbRkPvoy5yrtjZgPrT_RHDWm2TnlJiQsMu2eHMid8ZrDjwuAtbQVfrNmdov-ZgVQfdeHphFFpcVLV7vXDafHApaj6QS8_VKcaU0SgVUTPwBekHbRiOQg4_KOIrhF1knue18zB5" alt="How to Add Push Notifications to a Flutter App" width="880" height="289"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Navigate to the project settings menu in the Firebase console.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;&lt;em&gt;Cloud Messaging&lt;/em&gt;&lt;/strong&gt; tab, you’ll be able to find the &lt;strong&gt;&lt;em&gt;Firebase Server Key&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;Server ID&lt;/em&gt;&lt;/strong&gt;. Navigate back to your OneSignal dashboard and copy and paste those values into the appropriate fields under &lt;strong&gt;&lt;em&gt;OneSignal Google Android(FCM) Configuration&lt;/em&gt;&lt;/strong&gt; like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t6ocd7YL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/ASOexI_mhJTL_5onk8wRc9V2vf1IMGT2ENa5dHo6E7nkrx0YqVCTkwWdLD9rCohZwWbKkOzvJv-GidMq7i6kw2ZwXsd1qGhPlvHxjZGSuCrF-4Y4EsIrvT1VH8KUTtgGLMvoGJNg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t6ocd7YL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/ASOexI_mhJTL_5onk8wRc9V2vf1IMGT2ENa5dHo6E7nkrx0YqVCTkwWdLD9rCohZwWbKkOzvJv-GidMq7i6kw2ZwXsd1qGhPlvHxjZGSuCrF-4Y4EsIrvT1VH8KUTtgGLMvoGJNg" alt="How to Add Push Notifications to a Flutter App" width="880" height="344"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Input your Firebase Server Key and Sender ID in OneSignal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After clicking on &lt;strong&gt;&lt;em&gt;Save &amp;amp; Continue&lt;/em&gt;&lt;/strong&gt; , you’ll be asked to select your target SDK. Choose &lt;strong&gt;&lt;em&gt;Flutter&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CLUyx1oN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/WccnkK7Gq2_BySBjkOYOt05_ELqpNySyF-ILU4-rkt1T5pUgBFemOV8IKS8DKZQHC9aLH6vUNcj5QEYyS_0dK5EB4W-vJqKCjilpFwh2Zj1YvH3o20TVp7OFpaNp7lkYyH5j8AxB" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CLUyx1oN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/WccnkK7Gq2_BySBjkOYOt05_ELqpNySyF-ILU4-rkt1T5pUgBFemOV8IKS8DKZQHC9aLH6vUNcj5QEYyS_0dK5EB4W-vJqKCjilpFwh2Zj1YvH3o20TVp7OFpaNp7lkYyH5j8AxB" alt="How to Add Push Notifications to a Flutter App" width="880" height="390"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Choose Flutter as your target SDK.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;&lt;em&gt;Save &amp;amp; Continue&lt;/em&gt;&lt;/strong&gt; again and then click &lt;strong&gt;&lt;em&gt;Done&lt;/em&gt;&lt;/strong&gt; at the bottom of the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UQUAncBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/SYLtgXBKUAImb3MNUWVAoO0VuzFuSJw9-ZQ9OarySTix_pYGiniLb10SueR0-LAEtW7W8rsjmfRHvMVUQuSvVGs6YREP1gvzXs9R_Phs0rgeyzhz2insoyqxR0CuCdQth4vEaHWO" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UQUAncBl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/SYLtgXBKUAImb3MNUWVAoO0VuzFuSJw9-ZQ9OarySTix_pYGiniLb10SueR0-LAEtW7W8rsjmfRHvMVUQuSvVGs6YREP1gvzXs9R_Phs0rgeyzhz2insoyqxR0CuCdQth4vEaHWO" alt="How to Add Push Notifications to a Flutter App" width="880" height="356"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;View your app ID in OneSignal to finish the Google Android FCM configuration process.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, you should see your app dashboard on OneSignal under the _ &lt;strong&gt;Settings&lt;/strong&gt; &lt;em&gt;&amp;gt;&lt;/em&gt; &lt;strong&gt;Platforms&lt;/strong&gt; _ tab. On this page under _ &lt;strong&gt;Native App&lt;/strong&gt; _ _ &lt;strong&gt;platforms&lt;/strong&gt; &lt;em&gt;, you should see an **_Active&lt;/em&gt;** tag next to _ &lt;strong&gt;Google Android,&lt;/strong&gt; _ which means you’re ready to send notifications to users using the Android version of your Flutter app. If you followed the iOS setup, you should see an _ &lt;strong&gt;Active&lt;/strong&gt; _ tag next to _ &lt;strong&gt;Apple iOS&lt;/strong&gt; _.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bOKNeprp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/dfx8y7Ye_Ce1QSPauqCq4s9ULg8MDI6vA8_dkjjFMB3-vgOWyGvRfWZSIA5GRFx7qwIFNf88OkEZ71R6V0Xtd1DcwpFeIUBOck3VFkdHz3Ck3Sf3-dW3IeJ4d9chg08Ur5HPei5U" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bOKNeprp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/dfx8y7Ye_Ce1QSPauqCq4s9ULg8MDI6vA8_dkjjFMB3-vgOWyGvRfWZSIA5GRFx7qwIFNf88OkEZ71R6V0Xtd1DcwpFeIUBOck3VFkdHz3Ck3Sf3-dW3IeJ4d9chg08Ur5HPei5U" alt="How to Add Push Notifications to a Flutter App" width="880" height="420"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;An "Active" tag is displayed next to Google Android in the OneSignal dashboard.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Part 2: Creating a Flutter App
&lt;/h2&gt;

&lt;p&gt;So the first thing you need to do after creating a new Flutter project is to add the &lt;code&gt;onesignal_flutter&lt;/code&gt; dependency. To do that, visit &lt;a href="https://pub.dev/"&gt;pub.dev&lt;/a&gt; and search for &lt;strong&gt;&lt;em&gt;onesignal_flutter&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cYWYAPNf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/b6AGMy9QnDBPYEgM4Eb5ik9UbU6Dis24p3W2-VhGAo7PUUwT7ekALaYP1CY_lQBeFvA5j6QovvAErgLmzQKuvyJ_0wixkiLsqPM43VbwWYzyUnlKV-YrsLrPXgCbcqDPwVrQ0ZXv" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cYWYAPNf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/b6AGMy9QnDBPYEgM4Eb5ik9UbU6Dis24p3W2-VhGAo7PUUwT7ekALaYP1CY_lQBeFvA5j6QovvAErgLmzQKuvyJ_0wixkiLsqPM43VbwWYzyUnlKV-YrsLrPXgCbcqDPwVrQ0ZXv" alt="How to Add Push Notifications to a Flutter App" width="880" height="365"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Shows onesignal_flutter 3.2.7 in pub.dev.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add the latest version of &lt;code&gt;onesignal_flutter&lt;/code&gt; to &lt;code&gt;pubspec.yaml&lt;/code&gt; under the dependencies section with the version number from pub.dev.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--knGAiIDa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/Wp19ZvWDqfDQLnCkiaoCMbmMoqn-4D7LjHSVn03MdclYlQek5_3fA_WliBvaA1ceFvrL2rseaK0PJ2uytpodAL_hkalirkQb1ShSsq1r51uvtUqYj5uGEodXSdJJUR6efEZfBEUE" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--knGAiIDa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/Wp19ZvWDqfDQLnCkiaoCMbmMoqn-4D7LjHSVn03MdclYlQek5_3fA_WliBvaA1ceFvrL2rseaK0PJ2uytpodAL_hkalirkQb1ShSsq1r51uvtUqYj5uGEodXSdJJUR6efEZfBEUE" alt="How to Add Push Notifications to a Flutter App" width="880" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now open &lt;code&gt;main.dart&lt;/code&gt; and remove &lt;code&gt;MyHomePage()&lt;/code&gt; class (or just its contents).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FGzuSXUb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/9vBLaNXdBvwEfBfJV5WrZXHpkUob1UffT3-t7BHSuGqtM5QGxcK3BEEs7LCTHH0mqSAOXZ5PhKbbrfmpYIRKMgL4BBRsyM000KzTL481jKF2Dynf28eKoFJZkLaOvqMMuzNtlAAB" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FGzuSXUb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/9vBLaNXdBvwEfBfJV5WrZXHpkUob1UffT3-t7BHSuGqtM5QGxcK3BEEs7LCTHH0mqSAOXZ5PhKbbrfmpYIRKMgL4BBRsyM000KzTL481jKF2Dynf28eKoFJZkLaOvqMMuzNtlAAB" alt="How to Add Push Notifications to a Flutter App" width="880" height="453"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Remove the content of MyHomePage() from main.dart.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Create a new dart file named &lt;code&gt;home.dart&lt;/code&gt; (or continue in the &lt;code&gt;main.dart&lt;/code&gt; file) and create a stateful widget by the name &lt;code&gt;Home&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
@override
_HomeState createState() =&amp;gt; _HomeState();
}
class _HomeState extends State&amp;lt;Home&amp;gt; {
@override
Widget build(BuildContext context) {
return Container();
}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, build a simple UI by adding a few widgets like &lt;code&gt;Container&lt;/code&gt;, &lt;code&gt;Column&lt;/code&gt; and &lt;code&gt;Text&lt;/code&gt; in the &lt;code&gt;Home class&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Text("Hello"),
],
),
),
);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can add the &lt;strong&gt;&lt;em&gt;OneSignal App ID&lt;/em&gt;&lt;/strong&gt; which you can find in your OneSignal dashboard under the &lt;strong&gt;&lt;em&gt;Keys &amp;amp; IDs&lt;/em&gt;&lt;/strong&gt; tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c291on8B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/yqe9Or5bCachnjJN4PJeDNgV4gRnNdj0aTgCnQxewYEHX5u2n3gtfDK-Ie49aP0lnrY0ULOAmxPnPtdIcnABwKiO8dT8JSnPjcFkkyrUDYTYZqwZJc7gdtdbAtXHjBnQfcPuSElA" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c291on8B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/yqe9Or5bCachnjJN4PJeDNgV4gRnNdj0aTgCnQxewYEHX5u2n3gtfDK-Ie49aP0lnrY0ULOAmxPnPtdIcnABwKiO8dT8JSnPjcFkkyrUDYTYZqwZJc7gdtdbAtXHjBnQfcPuSElA" alt="How to Add Push Notifications to a Flutter App" width="880" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the &lt;strong&gt;&lt;em&gt;OneSignal App ID&lt;/em&gt;&lt;/strong&gt; and add it into your Flutter app in &lt;code&gt;main.dart&lt;/code&gt; as a constant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;static final String oneSignalAppId = "16090413-4b70-4c0b-a9f4-dd43c445ccee";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add SDK initialization code to your &lt;code&gt;XXX&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Future&amp;lt;void&amp;gt; initPlatformState() async {
   OneSignal.shared.setAppId(oneSignalAppId);
   OneSignal.shared
       .promptUserForPushNotificationPermission()
       .then((accepted) {});
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, initialize state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@override
 void initState() {
   super.initState();
   initPlatformState();
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Complete Code(main.dart)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import 'package:flutter/material.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';

class Home extends StatefulWidget {
 const Home({Key? key}) : super(key: key);

 @override
 _HomeState createState() =&amp;gt; _HomeState();
}

class _HomeState extends State&amp;lt;Home&amp;gt; {
 @override
 void initState() {
   super.initState();
   initPlatformState();
 }

 static final String oneSignalAppId = "16090413-4b70-4c0b-a9f4-dd43c445ccee";
 Future&amp;lt;void&amp;gt; initPlatformState() async {
   OneSignal.shared.setAppId(oneSignalAppId);
   OneSignal.shared
       .promptUserForPushNotificationPermission()
       .then((accepted) {});
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Container(
       child: Column(
         mainAxisAlignment: MainAxisAlignment.center,
         crossAxisAlignment: CrossAxisAlignment.center,
         children: const [
           Text("Hello"),
         ],
       ),
     ),
   );
 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run your Flutter app on your physical device or emulator.&lt;/p&gt;

&lt;p&gt;In case you recieve &lt;code&gt;compileSdkVersion error&lt;/code&gt; then go to your &lt;code&gt;app-level build.gradle&lt;/code&gt; file and update &lt;code&gt;compileSDKVersion&lt;/code&gt; to &lt;strong&gt;31&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.flutter_onesignal"
minSdkVersion 16
compileSdkVersion 31
targetSdkVersion 30
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get &lt;code&gt;Kotlin Version error&lt;/code&gt; then go to your &lt;code&gt;build.gradle&lt;/code&gt; file in the root of your Android directory and upgrade your &lt;code&gt;Kotlin_version&lt;/code&gt; to the latest version (&lt;code&gt;ext.kotlin_version = '1.5.10'&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;For the iOS version, there’s are some additional steps that need to be completed. The first one is to add an iOS service extension.&lt;/p&gt;

&lt;p&gt;The OneSignalNotificationServiceExtension allows your application (in iOS) to receive rich notifications with images and buttons, along with badges and &lt;a href="https://documentation.onesignal.com/docs/confirmed-deliveries"&gt;Confirmed Deliveries&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first step is to navigate to your Flutter project's iOS folder and open the &lt;code&gt;.xcworkspace&lt;/code&gt; file in Xcode. You can use the &lt;code&gt;open ios/Runner.xcworkspace&lt;/code&gt; command in the terminal to do the same.&lt;/p&gt;

&lt;p&gt;Then select &lt;strong&gt;&lt;em&gt;File&lt;/em&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;em&gt;New&lt;/em&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;em&gt;Target&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KCoAsNB5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/2ahVy90kNsCUL-FvxQhdZnD_1BMhepqGL27kHRoiawMzuYssIT0jYtMIPxLm3-iJkAcioce66I_RJj4hAYqI7RGLmNCf5xC9iNy8YJy1ZYdIIJpcHizth0GeSB3wOgxpGfuQ9xpK" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KCoAsNB5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/2ahVy90kNsCUL-FvxQhdZnD_1BMhepqGL27kHRoiawMzuYssIT0jYtMIPxLm3-iJkAcioce66I_RJj4hAYqI7RGLmNCf5xC9iNy8YJy1ZYdIIJpcHizth0GeSB3wOgxpGfuQ9xpK" alt="How to Add Push Notifications to a Flutter App" width="880" height="637"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;&lt;em&gt;Notification Service Extension&lt;/em&gt;&lt;/strong&gt; and press &lt;strong&gt;&lt;em&gt;Next&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KYJ2te2f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/-HhMpA3cmMlEDakFbw55JB1QVkHfpYDG-aUYtrqLYfZc9GQJrsXVSmfuKRGBUkouYNm-MOoyLNV6DPi8Jzh6SmBKAqv5bSzL4e-TkA0ksFLt3UHDjt0OEbaPODqYxfjZ8AcjWCDP" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KYJ2te2f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/-HhMpA3cmMlEDakFbw55JB1QVkHfpYDG-aUYtrqLYfZc9GQJrsXVSmfuKRGBUkouYNm-MOoyLNV6DPi8Jzh6SmBKAqv5bSzL4e-TkA0ksFLt3UHDjt0OEbaPODqYxfjZ8AcjWCDP" alt="How to Add Push Notifications to a Flutter App" width="880" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the product name as &lt;strong&gt;&lt;em&gt;OneSignalNotificationExtension&lt;/em&gt;&lt;/strong&gt; and change the language to _ &lt;strong&gt;Object-C&lt;/strong&gt; _ or &lt;strong&gt;&lt;em&gt;Swift&lt;/em&gt;&lt;/strong&gt; according to your needs. The team account should be your account or your organization’s account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b34Tgg93--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/1akC8xbAiWfhyifJMOCqso3-eW9m3hhHcpRGqu4qanre1Q997EIPGvP5yN0VJFjxJkgfeDN24oAd6z_-NIDQ0S3yUEhtiTQUTHIRISxaBg5R-fQiFPsj7cxeTkJ4FArjOnz3BRDE" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b34Tgg93--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/1akC8xbAiWfhyifJMOCqso3-eW9m3hhHcpRGqu4qanre1Q997EIPGvP5yN0VJFjxJkgfeDN24oAd6z_-NIDQ0S3yUEhtiTQUTHIRISxaBg5R-fQiFPsj7cxeTkJ4FArjOnz3BRDE" alt="How to Add Push Notifications to a Flutter App" width="880" height="534"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Add the product name as OneSignalNotificationExtension in Flutter.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Click the _ &lt;strong&gt;Finish&lt;/strong&gt; _ at the bottom right corner of the window, but when prompted to “Activate” scheme click on _ &lt;strong&gt;Cancel&lt;/strong&gt; _.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uZEJ1mrX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/_lrzrxMW6I41eYSoPBx5kGdmk2j1HYCZHr64toNYOzwFevPFi68e8zZez_hw4Feqzido91O5Ks_c5CYe8s0BjYfFwCCZG21g332n-X53JqjujSIRjeH5qZovFnQj8k0XYf_H3UaP" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uZEJ1mrX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/_lrzrxMW6I41eYSoPBx5kGdmk2j1HYCZHr64toNYOzwFevPFi68e8zZez_hw4Feqzido91O5Ks_c5CYe8s0BjYfFwCCZG21g332n-X53JqjujSIRjeH5qZovFnQj8k0XYf_H3UaP" alt="How to Add Push Notifications to a Flutter App" width="880" height="930"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Click "Cancel" when prompted to activate the scheme.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;By canceling, you are keeping Xcode set to debug your app rather than using the extension. If you select _ &lt;strong&gt;Activate&lt;/strong&gt; _ by accident, you can simply switch back to debug your app in Xcode (next to the Play button).&lt;/p&gt;

&lt;p&gt;Now, open the Xcode project settings and select the _ &lt;strong&gt;OneSignalNotificationServiceExtension&lt;/strong&gt; _ target. Under the _ &lt;strong&gt;Deployment info&lt;/strong&gt; _ section on this page, select iOS 10.0 as the target unless there are any reason for using higher versions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_CwebazN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/pm2Nwawvmko5SA50hUUKzzLEdWA4yVF83qTSk4ORSLGgu7xWVY2ahRKiNls8xidSL6wcI2E4SchyoRr0nzpUnaS9bCBBzN0iDWCYmaSock0CRJrDLBplqx3Wxs6g6ZqlhOBXUYIw" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_CwebazN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/pm2Nwawvmko5SA50hUUKzzLEdWA4yVF83qTSk4ORSLGgu7xWVY2ahRKiNls8xidSL6wcI2E4SchyoRr0nzpUnaS9bCBBzN0iDWCYmaSock0CRJrDLBplqx3Wxs6g6ZqlhOBXUYIw" alt="How to Add Push Notifications to a Flutter App" width="880" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, close the Xcode project and go back to your Flutter development IDE. In the &lt;code&gt;/ios&lt;/code&gt; directory of your project, open the &lt;strong&gt;&lt;em&gt;Podfile&lt;/em&gt;&lt;/strong&gt; and add the following lines outside of the main target (they should be at the same level as your main target):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;target 'OneSignalNotificationServiceExtension' do
use_frameworks!
pod 'OneSignalXCFramework', '&amp;gt;= 3.4.3', '&amp;lt; 4.0'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DbProiqh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/YYa5t-_B9sK-Ca-tdjFUxSfnB0iumq7FPZ86YKkJty66WTyw5liQTv_dKyjcznYXOhxOvG2SYUfKZ7yvmzZWjP7uau9CzHdmicoWKIbWnpAL4Hwy8TOr-VUrBK0V5KTt6T8Hq0K_" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DbProiqh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/YYa5t-_B9sK-Ca-tdjFUxSfnB0iumq7FPZ86YKkJty66WTyw5liQTv_dKyjcznYXOhxOvG2SYUfKZ7yvmzZWjP7uau9CzHdmicoWKIbWnpAL4Hwy8TOr-VUrBK0V5KTt6T8Hq0K_" alt="How to Add Push Notifications to a Flutter App" width="880" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, make sure to uncomment the platform line at the top of the &lt;strong&gt;&lt;em&gt;Podfile&lt;/em&gt;.&lt;/strong&gt; It can be iOS version 9.0 or higher according to your needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lUqMNHPd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/kbNDzO4R_o82ntNP04yqr07iV1ajyFDc3TQsqq07dNfbR77ZvI_2aV8oDdNNDIN1_PBHXD4CsFkQlnS7hzmLBGAjwCXp_6bO6y61QkwxG4_Jv40aENiwASNujDLTM2OfxXLd-JH7" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lUqMNHPd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/kbNDzO4R_o82ntNP04yqr07iV1ajyFDc3TQsqq07dNfbR77ZvI_2aV8oDdNNDIN1_PBHXD4CsFkQlnS7hzmLBGAjwCXp_6bO6y61QkwxG4_Jv40aENiwASNujDLTM2OfxXLd-JH7" alt="How to Add Push Notifications to a Flutter App" width="880" height="552"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now Open the terminal, cd to the ios directory, and run pod install.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7p7kULp3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/bMNOc-BQBzq0F19UZg-Q2ctZGX2M43GB3xLx5pgnOEOZLn2PNXiNotZGZ0vkXng0c8M-qkE_Vd8cc2NqMEwQVbDBW6tRioxz7XS9TrEJlOkRGdFaXeMf4_ChF6hppuQ13dWFnjW9" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7p7kULp3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/bMNOc-BQBzq0F19UZg-Q2ctZGX2M43GB3xLx5pgnOEOZLn2PNXiNotZGZ0vkXng0c8M-qkE_Vd8cc2NqMEwQVbDBW6tRioxz7XS9TrEJlOkRGdFaXeMf4_ChF6hppuQ13dWFnjW9" alt="How to Add Push Notifications to a Flutter App" width="880" height="178"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see the error below, remove &lt;code&gt;#&lt;/code&gt; from the above in front of &lt;code&gt;use_frameworks!&lt;/code&gt; and try again.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;- Runner (true) and OneSignalNotificationServiceExtension (false) do not both set use_frameworks!.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Open the &lt;code&gt;&amp;lt;project-name&amp;gt;.xcworkspace&lt;/code&gt; file. In your project, in the &lt;code&gt;OneSignalNotificationServiceExtension/&lt;/code&gt; folder, open &lt;code&gt;NotificationService.m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mh9P5mko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/6LNQ1cUOlh4Wsp4rVr6Y1wgVPtDCRpgOR48BWeTqtoOHrEoUGHReEyN5sVJfha1SqbEa1tV4G0KAqnHhSQxvvb8-oNttLBTGk6EKvSAQhtgfjta_N5EmFqln7Bg0anT_c7HxUVp4" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mh9P5mko--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh4.googleusercontent.com/6LNQ1cUOlh4Wsp4rVr6Y1wgVPtDCRpgOR48BWeTqtoOHrEoUGHReEyN5sVJfha1SqbEa1tV4G0KAqnHhSQxvvb8-oNttLBTGk6EKvSAQhtgfjta_N5EmFqln7Bg0anT_c7HxUVp4" alt="How to Add Push Notifications to a Flutter App" width="708" height="524"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Replace the whole file contents with the following code for Objective-C:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#import &amp;lt;OneSignal/OneSignal.h&amp;gt;
#import "NotificationService.h"
@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNNotificationRequest *receivedRequest;
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end
@implementation NotificationService
- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
   self.receivedRequest = request;
   self.contentHandler = contentHandler;
   self.bestAttemptContent = [request.content mutableCopy];

   //If your SDK version is &amp;lt; 3.5.0 uncomment and use this code:
   /*
   [OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest
                      withMutableNotificationContent:self.bestAttemptContent];
   self.contentHandler(self.bestAttemptContent);
   */

   /* DEBUGGING: Uncomment the 2 lines below and comment out the one above to ensure this extension is excuting
                 Note, this extension only runs when mutable-content is set
                 Setting an attachment or action buttons automatically adds this */
   // NSLog(@"Running NotificationServiceExtension");
   // self.bestAttemptContent.body = [@"[Modified] " stringByAppendingString:self.bestAttemptContent.body];

   // Uncomment this line to set the default log level of NSE to VERBOSE so we get all logs from NSE logic
   //[OneSignal setLogLevel:ONE_S_LL_VERBOSE visualLevel:ONE_S_LL_NONE];
   [OneSignal didReceiveNotificationExtensionRequest:self.receivedRequest
                      withMutableNotificationContent:self.bestAttemptContent
                                  withContentHandler:self.contentHandler];
}
- (void)serviceExtensionTimeWillExpire {
   // Called just before the extension will be terminated by the system.
   // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.

   [OneSignal serviceExtensionTimeWillExpireRequest:self.receivedRequest withMutableNotificationContent:self.bestAttemptContent];

   self.contentHandler(self.bestAttemptContent);
}
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Swift, replace it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import UserNotifications

import OneSignal

class NotificationService: UNNotificationServiceExtension {

   var contentHandler: ((UNNotificationContent) -&amp;gt; Void)?
   var receivedRequest: UNNotificationRequest!
   var bestAttemptContent: UNMutableNotificationContent?

   override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -&amp;gt; Void) {
       self.receivedRequest = request
       self.contentHandler = contentHandler
       self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

       if let bestAttemptContent = bestAttemptContent {
           //If your SDK version is &amp;lt; 3.5.0 uncomment and use this code:
           /*
           OneSignal.didReceiveNotificationExtensionRequest(self.receivedRequest, with: self.bestAttemptContent)
           contentHandler(bestAttemptContent)
           */

           /* DEBUGGING: Uncomment the 2 lines below to check this extension is excuting
                         Note, this extension only runs when mutable-content is set
                         Setting an attachment or action buttons automatically adds this */
           //OneSignal.setLogLevel(.LL_VERBOSE, visualLevel: .LL_NONE)
           //bestAttemptContent.body = "[Modified] " + bestAttemptContent.body

           OneSignal.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)
       }
   }

   override func serviceExtensionTimeWillExpire() {
       // Called just before the extension will be terminated by the system.
       // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
       if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
           OneSignal.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
           contentHandler(bestAttemptContent)
       }
   }

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also need to enable an &lt;strong&gt;App Group&lt;/strong&gt; to use &lt;a href="https://documentation.onesignal.com/docs/confirmed-deliveries"&gt;Confirmed Deliveries&lt;/a&gt; and increment/decrement &lt;a href="https://documentation.onesignal.com/docs/badges"&gt;Badges&lt;/a&gt; through push notifications. For that, you can follow the &lt;a href="https://documentation.onesignal.com/docs/ios-sdk-app-groups-setup"&gt;iOS SDK App Groups setup guide&lt;/a&gt; to add the OneSignal App Group in your app.&lt;/p&gt;

&lt;p&gt;Now you need to enable &lt;strong&gt;push capability for iOS apps&lt;/strong&gt;. Open your &lt;code&gt;.xcworkspace file&lt;/code&gt; in Xcode.&lt;/p&gt;

&lt;p&gt;In the main app target, select &lt;strong&gt;&lt;em&gt;Signing &amp;amp; Capabilities&lt;/em&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;em&gt;All&lt;/em&gt;&lt;/strong&gt; &amp;gt; &lt;strong&gt;&lt;em&gt;+ Capability&lt;/em&gt;&lt;/strong&gt; and then search "push." Double-click on _ &lt;strong&gt;Push Notifications&lt;/strong&gt; _ to enable it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IMNfvYae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/DWJI_pmSWCdbnZCYSqZbJRjrQk1qfmCaT1KHQRTxeTfVJBBKgL9_AVB6j4SdLjTsZEymjfrXu9orM9T7NzfxFQew2_yI6H_TCgtjk4RlpXSKi9d-jldSLpRL93ozoRPMbOmfttcX" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IMNfvYae--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/DWJI_pmSWCdbnZCYSqZbJRjrQk1qfmCaT1KHQRTxeTfVJBBKgL9_AVB6j4SdLjTsZEymjfrXu9orM9T7NzfxFQew2_yI6H_TCgtjk4RlpXSKi9d-jldSLpRL93ozoRPMbOmfttcX" alt="How to Add Push Notifications to a Flutter App" width="880" height="664"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, enable &lt;strong&gt;&lt;em&gt;Background Modes&lt;/em&gt;&lt;/strong&gt; and check &lt;strong&gt;&lt;em&gt;Remote Notifications&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O6pGTd_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/oaedkTvazpCH9OaAEJ2mTnEH1k5KCcDkuDEWzBgQTSIRUNa1gUJji9G9ljuXsZEXIhctWjSPIG5sUY4TKx28dn7juuDPrA8BHSU1H-a51Kdr7157o-PLOKLovp6w7mIJT-5zuppa" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O6pGTd_A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/oaedkTvazpCH9OaAEJ2mTnEH1k5KCcDkuDEWzBgQTSIRUNa1gUJji9G9ljuXsZEXIhctWjSPIG5sUY4TKx28dn7juuDPrA8BHSU1H-a51Kdr7157o-PLOKLovp6w7mIJT-5zuppa" alt="How to Add Push Notifications to a Flutter App" width="880" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you are ready to send notifications from the OneSignal dashboard!&lt;/p&gt;

&lt;p&gt;Back in your OneSignal dashboard, click on &lt;strong&gt;Messages&lt;/strong&gt; in the top navigation menu and select _ &lt;strong&gt;Push&lt;/strong&gt; _ from the sub-menu. Click the &lt;strong&gt;&lt;em&gt;+New Push&lt;/em&gt;&lt;/strong&gt; button to create your first notification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Notifications are enabled on Android devices by default if you have disabled your notifications, make sure you &lt;a href="https://support.google.com/android/answer/9079661?hl=en#zippy=%2Coption-show-all-notifications"&gt;enable them again&lt;/a&gt;. If you're sending a notification to an iOS device, you will need to opt-in to notifications in your app settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--99MlZA1I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/q5c86f4bIcxmL3rgkG-z-82Go1BiAnOQMLniEecZZBe0rUUewtF8bBY_HuXB83HDS2_AqkBIuvZjn9QoohdwVJAwwM8Sjb-tBD4VSaSyRbxwWm6KrEClQg7wJeR85dSHl8bUe8o_" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--99MlZA1I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh3.googleusercontent.com/q5c86f4bIcxmL3rgkG-z-82Go1BiAnOQMLniEecZZBe0rUUewtF8bBY_HuXB83HDS2_AqkBIuvZjn9QoohdwVJAwwM8Sjb-tBD4VSaSyRbxwWm6KrEClQg7wJeR85dSHl8bUe8o_" alt="How to Add Push Notifications to a Flutter App" width="880" height="213"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Click the "+New Push" button in your OneSignal dashboard.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You will be redirected to a new window that will allow you to customize your push notification. Under _ &lt;strong&gt;Audience&lt;/strong&gt; _, make sure that _ &lt;strong&gt;Send to Subscribed Users&lt;/strong&gt; _ is selected. Then, create your message by adding your message title, content, and image. Because this is the first notification your subscribers will receive, you may choose to craft a simple welcome message to confirm that they've been subscribed and reinforce the value that notifications will provide.&lt;/p&gt;

&lt;p&gt;Under the _ &lt;strong&gt;Delivery Schedule&lt;/strong&gt; _ section, select _ &lt;strong&gt;Immediately&lt;/strong&gt; _ and _ &lt;strong&gt;Send to everyone at the same time&lt;/strong&gt; _ to send to all your current push &lt;strong&gt;subscribers&lt;/strong&gt;. If you have just finished setting up your OneSignal account, chances are you're the first and only &lt;strong&gt;subscriber&lt;/strong&gt;. If your app or website is heavily trafficked and other users have already opted in to receive push notifications, you may want to select &lt;em&gt;&lt;strong&gt;Send to a particular segment(s)&lt;/strong&gt;&lt;/em&gt; to test your message out on a specific audience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cv__N2yr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/DniDr03ChMf0gKDZF5slIh9qavCZlMNmm4yFdDHQS4Jowjh2NP3NzJ-Qyf8SfR-5am7PUbkY5dy0TlIXj7RxuQox8UAWvGs4Wnmn5W752ISgtaO1pCw-ENOiRC5JZF2poo1Q2aUr" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cv__N2yr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/DniDr03ChMf0gKDZF5slIh9qavCZlMNmm4yFdDHQS4Jowjh2NP3NzJ-Qyf8SfR-5am7PUbkY5dy0TlIXj7RxuQox8UAWvGs4Wnmn5W752ISgtaO1pCw-ENOiRC5JZF2poo1Q2aUr" alt="How to Add Push Notifications to a Flutter App" width="880" height="419"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Specify the audience for your push notification in OneSignal.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Add your message title and copy and preview how your message looks on your target device.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ctvClu1R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/9xiwSSoMPYiEmP0i67DsfmKl5RskyDhPCPxiKkBgIIpRHE0u37n3XFakDxcwIpT5g8IPVCTRv5EPpJke1H25dFd2IKe9MG-mMHl_0Cuh7bLcrOoj_9WMpTlckRZKU1p0gCQU01u3" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ctvClu1R--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh5.googleusercontent.com/9xiwSSoMPYiEmP0i67DsfmKl5RskyDhPCPxiKkBgIIpRHE0u37n3XFakDxcwIpT5g8IPVCTRv5EPpJke1H25dFd2IKe9MG-mMHl_0Cuh7bLcrOoj_9WMpTlckRZKU1p0gCQU01u3" alt="How to Add Push Notifications to a Flutter App" width="880" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you're ready to send your message, click on the blue _ &lt;strong&gt;Review and Send&lt;/strong&gt; _ button at the bottom of the screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OVSA7Ds6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/34f-jBgJ13ypRXN0N_FoonL73H8qScgqUENeOPK6hp7MU4fGyn0KCeAsftUlB71jh5kdQwuiQP5_Rw5nH7IL60nUrkV-DFBo7EABsKnDMRg-P9UC-S6pqrCkBGmhnCj0tb7D1AhF" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OVSA7Ds6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/34f-jBgJ13ypRXN0N_FoonL73H8qScgqUENeOPK6hp7MU4fGyn0KCeAsftUlB71jh5kdQwuiQP5_Rw5nH7IL60nUrkV-DFBo7EABsKnDMRg-P9UC-S6pqrCkBGmhnCj0tb7D1AhF" alt="How to Add Push Notifications to a Flutter App" width="880" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A small popup will appear for you to review your message. Once you are satisfied, click on the blue _ &lt;strong&gt;Send Message&lt;/strong&gt; _ button. You should receive a push notification on your device! 🚀&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1jZBX5a8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/gHVM6DAHNffI_bTGhz1SRB5Sa0bxQUkTCrclZpn6vDALkUpjegZspNGOFBHz0jwZmiQpAup_xDBlXmFg1EURwCVRW0S5bramwtss3rH6GJWRZCjjylW24ZaIuhkHjbKjPOwMWkZG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1jZBX5a8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/gHVM6DAHNffI_bTGhz1SRB5Sa0bxQUkTCrclZpn6vDALkUpjegZspNGOFBHz0jwZmiQpAup_xDBlXmFg1EURwCVRW0S5bramwtss3rH6GJWRZCjjylW24ZaIuhkHjbKjPOwMWkZG" alt="How to Add Push Notifications to a Flutter App" width="880" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re done &lt;strong&gt;🎊&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;You’ll receive the notification on your physical device that you used to run your Flutter app or on the emulator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P3fhGWgL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/Sbz_v6cItylXPlj4D2BvbajJwSX_uGXsvOsriLB9ZjWs4tgB6gmXJrJOaGOWWwN9VccqZMPlJDCLqvuxI5TuRZPLI5ZUGpaoiuTXiqt6Pz49-DGH3V3sVlF2gERENOXgUqE_TMve" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P3fhGWgL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://lh6.googleusercontent.com/Sbz_v6cItylXPlj4D2BvbajJwSX_uGXsvOsriLB9ZjWs4tgB6gmXJrJOaGOWWwN9VccqZMPlJDCLqvuxI5TuRZPLI5ZUGpaoiuTXiqt6Pz49-DGH3V3sVlF2gERENOXgUqE_TMve" alt="How to Add Push Notifications to a Flutter App" width="880" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the message sends, you'll be directed to the &lt;strong&gt;&lt;em&gt;Delivery&lt;/em&gt;&lt;/strong&gt; page where you can view the send report and track engagement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Still have questions?
&lt;/h3&gt;

&lt;p&gt;Check out our complete &lt;a href="https://documentation.onesignal.com/docs/flutter-sdk-setup"&gt;OneSignal Flutter SDK Documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post was guest authored by &lt;strong&gt;Akanksha Singh&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>mobiledev</category>
      <category>flutter</category>
      <category>dart</category>
      <category>programmer</category>
    </item>
    <item>
      <title>Thread safety and Learning in Rust</title>
      <dc:creator>onesignaldevs</dc:creator>
      <pubDate>Fri, 02 Jul 2021 17:00:13 +0000</pubDate>
      <link>https://forem.com/onesignal/thread-safety-and-learning-in-rust-1p83</link>
      <guid>https://forem.com/onesignal/thread-safety-and-learning-in-rust-1p83</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fonesignal.com%2Fblog%2Fcontent%2Fimages%2F2021%2F07%2FUnderstanding-Thread-Safety-in-Rust.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fonesignal.com%2Fblog%2Fcontent%2Fimages%2F2021%2F07%2FUnderstanding-Thread-Safety-in-Rust.png" alt="Thread safety and Learning in Rust"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Programming interviews can be a great learning experience for interviewers as well as interviewees. In fact, I recently had the opportunity to learn something new about Rust while interviewing an engineering candidate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Background About our Interview Process
&lt;/h3&gt;

&lt;p&gt;At OneSignal, we don't believe in handing someone a problem and watching them flounder with zero autocomplete and zero docs. Engineers who apply to join our team are allowed to use their own environments, access documentation, and treat the interviewer as a resource. If a candidate can solve a problem with some gentle assistance from the interviewer or by asking a few questions, then they're much closer to the real development experience. Senior engineers on our teams frequently act as resources for other developers, and if a candidate can get to a solution by treating the interviewer like a colleague then they're halfway home. Candidates often learn something new from the interviewer, and we've had great feedback on our process.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Learning
&lt;/h3&gt;

&lt;p&gt;The candidate was working on a problem involving concurrency and needed to share the same &lt;code&gt;std::sync::mpsc::Sender&lt;/code&gt; with multiple threads concurrently. They wrote code analogous to the following (extremely simplified) example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use std::sync::{RwLock, Arc, mpsc::channel};

pub fn main() {
    let (tx, rx) = channel();

    let x = Arc::new(RwLock::new(tx));

    std::thread::spawn(move || {
       x.read().unwrap().send(4u8).unwrap(); 
    });

    dbg!(rx.recv().unwrap());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Code that attempts to share a &lt;code&gt;Sender&amp;lt;u8&amp;gt;&lt;/code&gt; across threads using an &lt;code&gt;RwLock&lt;/code&gt;





&lt;p&gt;Now you may initially be thinking that it's just wrong to put a channel sender inside of an &lt;code&gt;RwLock&lt;/code&gt; — and you'd be right. Both of these types are useful for cross-thread synchronization, and it's odd to use both of them together. Each &lt;code&gt;Sender&lt;/code&gt; is intended to be owned by a single thread, and if you need to coordinate between multiple threads you should be calling &lt;code&gt;Sender::clone()&lt;/code&gt;. However, I urge you to recall that this was taking place during an interview, and concurrency was not the only thing going on in this program. The candidate had made other architectural decisions that led them here, and this small example has removed all of that context. Rust is a complex language with many types of synchronization primitives available, and during the time constraints of an interview, it's not unreasonable for someone to combine them in unusual ways.&lt;/p&gt;

&lt;p&gt;The Rust compiler had a much stricter and less egalitarian view on this combination of types, however. When we tried to compile it, we were greeted with this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[E0277]: `Sender&amp;lt;u8&amp;gt;` cannot be shared between threads safely
   --&amp;gt; src/main.rs:8:5
    |
8 | std::thread::spawn(move || {
    | ^^^^^^^^^^^^^^^^^^ `Sender&amp;lt;u8&amp;gt;` cannot be shared between threads safely
    |
    = help: the trait `Sync` is not implemented for `Sender&amp;lt;u8&amp;gt;`
    = note: required because of the requirements on the impl of `Sync` for `RwLock&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;`
    = note: required because of the requirements on the impl of `Send` for `Arc&amp;lt;RwLock&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;`
    = note: required because it appears within the type `[closure@src/main.rs:8:24: 10:6]`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Compiler error that is generated by the above program





&lt;p&gt;This confused both of us a lot. I know that &lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; can be sent across threads safely — that's the whole point of it! We tried a few things (using &lt;code&gt;.write()&lt;/code&gt; on the &lt;code&gt;RwLock&lt;/code&gt; instead of &lt;code&gt;.read()&lt;/code&gt;)  but we couldn't get it to compile. Eventually, I wondered if the code would work if we used the simpler &lt;code&gt;Mutex&lt;/code&gt; instead of an &lt;code&gt;RwLock&lt;/code&gt;. I had the candidate try this instead, and the program works. Here's what that program looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;use std::sync::{Mutex, Arc, mpsc::channel};

pub fn main() {
    let (tx, rx) = channel();

    let x = Arc::new(Mutex::new(tx));

    std::thread::spawn(move || {
       x.lock().unwrap().send(4u8).unwrap(); 
    });

    dbg!(rx.recv().unwrap());
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
Program which uses &lt;code&gt;Mutex&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt; instead of &lt;code&gt;RwLock&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;&lt;/code&gt;





&lt;p&gt;Running this program produces the output that we expected from the first one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[src/main.rs:12] rx.recv().unwrap() = 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's worth pointing out that if you find yourself putting a &lt;code&gt;Sender&lt;/code&gt; inside some type of &lt;code&gt;Mutex&lt;/code&gt; or locking data structure, then you may want to reconsider your program's structure. But why does the &lt;code&gt;RwLock&lt;/code&gt; version fail, and the &lt;code&gt;Mutex&lt;/code&gt; version work?&lt;/p&gt;

&lt;p&gt;To answer this question, we need to consider the meaning of the &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt; traits, the &lt;code&gt;Sender&lt;/code&gt;, &lt;code&gt;RwLock&lt;/code&gt; and &lt;code&gt;Mutex&lt;/code&gt; types, and look closely at the implementations of &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt; for all of the types in question. Let's start with &lt;code&gt;RwLock&lt;/code&gt; and &lt;code&gt;Mutex&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  RwLock vs. Mutex
&lt;/h2&gt;

&lt;p&gt;Let's look at the differences between the two synchronization types that were used in this example, &lt;code&gt;RwLock&lt;/code&gt; and &lt;code&gt;Mutex&lt;/code&gt;. They are conceptually very similar — both allow you to guard a resource to ensure that it is accessed from multiple threads in a safe way. They have one important difference, however. &lt;code&gt;Mutex&lt;/code&gt; holds a lock for both reads and writes, whereas &lt;code&gt;RwLock&lt;/code&gt; treats reads and writes differently, allowing for multiple read locks to be taken in parallel but requiring exclusive access for write locks. The way that &lt;code&gt;RwLock&lt;/code&gt; enforces single writer, multiple reader rules is the same as the single &lt;code&gt;&amp;amp;mut&lt;/code&gt;, multiple &lt;code&gt;&amp;amp;&lt;/code&gt; reference rules for single-threaded code. Here's a visual of how the same sequence of reads &amp;amp; writes occurs with a &lt;code&gt;Mutex&lt;/code&gt; and an &lt;code&gt;RwLock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fonesignal.com%2Fblog%2Fcontent%2Fimages%2F2021%2F06%2FMutexVsRwLock.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fonesignal.com%2Fblog%2Fcontent%2Fimages%2F2021%2F06%2FMutexVsRwLock.png" alt="Thread safety and Learning in Rust"&gt;&lt;/a&gt;Visual on read vs write scheduling with &lt;code&gt;Mutex&lt;/code&gt; and &lt;code&gt;RwLock&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;As the diagram shows, when you have a read-heavy workload it is preferable to use an &lt;code&gt;RwLock&lt;/code&gt; to avoid blocking reader threads when it's not required. The same sequence of events takes much longer with a &lt;code&gt;Mutex&lt;/code&gt; than it does with an &lt;code&gt;RwLock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We thought that we could get away with using an &lt;code&gt;RwLock&lt;/code&gt; around our &lt;code&gt;Sender&lt;/code&gt; because the method that we needed to call (&lt;code&gt;Sender::send(&amp;amp;self, value: T)&lt;/code&gt;) requires an immutable (read) reference to the underlying &lt;code&gt;Sender&lt;/code&gt; rather than a mutable &lt;code&gt;&amp;amp;mut self&lt;/code&gt; reference. This implies that we could use &lt;code&gt;RwLock&lt;/code&gt; and &lt;code&gt;RwLock::read()&lt;/code&gt; to guard calls to &lt;code&gt;Sender::send()&lt;/code&gt;. This would allow us to spend much less time waiting on synchronization than if we used a &lt;code&gt;Mutex&lt;/code&gt; around the &lt;code&gt;Sender&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;Send&lt;/code&gt; trait is the simpler of the two traits to explain conceptually. A type is &lt;code&gt;Send&lt;/code&gt; if it is safe to transfer it across thread boundaries. This means that you can construct an instance of the type on one thread, transfer ownership to another thread, and &lt;code&gt;Drop&lt;/code&gt; it on that other thread. Most types in Rust are &lt;code&gt;Send&lt;/code&gt;, but there are a few exceptions. The most common non-&lt;code&gt;Send&lt;/code&gt; types are &lt;code&gt;Rc&lt;/code&gt; and all raw pointer types. The raw pointer types are not &lt;code&gt;Send&lt;/code&gt; because Rust cannot make any guarantees about the synchronization mechanisms that are in the underlying types, or when the pointers will be accessed. &lt;code&gt;Rc&lt;/code&gt; is not &lt;code&gt;Send&lt;/code&gt; because it uses a non-atomic reference counter which is not safe to send between threads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sync
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Sync&lt;/code&gt; is a bit trickier to explain. A type &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;Sync&lt;/code&gt; if and only if &lt;code&gt;&amp;amp;T&lt;/code&gt; is &lt;code&gt;Send&lt;/code&gt;. This means that it must be safe to share immutable references of &lt;code&gt;T&lt;/code&gt; between threads. A simpler way to think about this is to imagine if it would be safe to have multiple immutable references of type &lt;code&gt;T&lt;/code&gt; being read from in parallel from multiple threads. The most common non-&lt;code&gt;Sync&lt;/code&gt; types are &lt;code&gt;UnsafeCell&lt;/code&gt;, &lt;code&gt;Cell&lt;/code&gt;, and &lt;code&gt;RefCell&lt;/code&gt;. These types all rely on unsynchronized mutation, so they are inherently not thread-safe.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sender
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;std::sync::mpsc::Sender&lt;/code&gt; type is the producer-half of a multi-producer, single-consumer channel. It's intended to be used by calling &lt;code&gt;Sender::clone&lt;/code&gt; and using a single owned &lt;code&gt;Sender&lt;/code&gt; per-thread. This pattern permits &lt;code&gt;Sender&lt;/code&gt; to be non-&lt;code&gt;Sync&lt;/code&gt; because each individual instance of &lt;code&gt;Sender&lt;/code&gt; does not need to synchronize with itself across multiple threads. It only needs to be &lt;code&gt;Send&lt;/code&gt; so that instances can be moved from thread to thread with ownership staying consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send + Sync in our programs
&lt;/h2&gt;

&lt;p&gt;Let's look at the &lt;code&gt;Send&lt;/code&gt; &amp;amp; &lt;code&gt;Sync&lt;/code&gt; implementations for all of the types that are in our example programs. Our programs used &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; (works) and &lt;code&gt;Arc&amp;lt;RwLock&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; (does not work).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;impl&amp;lt;T: ?Sized + Send&amp;gt; Send for RwLock&amp;lt;T&amp;gt;
impl&amp;lt;T: ?Sized + Send + Sync&amp;gt; Sync for RwLock&amp;lt;T&amp;gt;

impl&amp;lt;T: ?Sized + Send&amp;gt; Send for Mutex&amp;lt;T&amp;gt;
impl&amp;lt;T: ?Sized + Send&amp;gt; Sync for Mutex&amp;lt;T&amp;gt;

impl&amp;lt;T: Send&amp;gt; Send for Sender&amp;lt;T&amp;gt;
impl&amp;lt;T&amp;gt; !Sync for Sender&amp;lt;T&amp;gt;

impl&amp;lt;T: ?Sized + Sync + Send&amp;gt; Send for Arc&amp;lt;T&amp;gt;
impl&amp;lt;T: ?Sized + Sync + Send&amp;gt; Sync for Arc&amp;lt;T&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;!Sync&lt;/code&gt; syntax indicates that &lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; is explicitly &lt;em&gt;not&lt;/em&gt; &lt;code&gt;Sync&lt;/code&gt;. The &lt;code&gt;?Sized&lt;/code&gt; syntax is not relevant to this discussion.&lt;/p&gt;

&lt;p&gt;There are a few important things to note here:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; is not &lt;code&gt;Sync&lt;/code&gt;. It's not safe to have multiple immutable references to a &lt;code&gt;Sender&lt;/code&gt; live across multiple threads at the same time. Considering that &lt;code&gt;Sender::send(&amp;amp;self)&lt;/code&gt; requires only an immutable reference, there must be some kind of interior mutability going on.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mutex&amp;lt;T&amp;gt;&lt;/code&gt; is both &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt; if &lt;code&gt;T&lt;/code&gt; inside of it is &lt;code&gt;Send&lt;/code&gt;. If something is inside of a &lt;code&gt;Mutex&lt;/code&gt;, it will never have multiple immutable references live at the same time since &lt;code&gt;Mutex&lt;/code&gt; does not allow multiple locks (read or write) to be taken at the same time. Because of this, &lt;code&gt;Mutex&amp;lt;T&amp;gt;&lt;/code&gt; effectively bypasses the restrictions of &lt;code&gt;Sync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;RwLock&amp;lt;T&amp;gt;&lt;/code&gt; is &lt;code&gt;Send&lt;/code&gt; where &lt;code&gt;T&lt;/code&gt; is &lt;code&gt;Send&lt;/code&gt;, but it is only &lt;code&gt;Sync&lt;/code&gt; if &lt;code&gt;T&lt;/code&gt; is both &lt;code&gt;Send + Sync&lt;/code&gt;. Since most types are &lt;code&gt;Send + Sync&lt;/code&gt;, this is a non-issue. It only causes problems because the &lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; within it is not &lt;code&gt;Sync&lt;/code&gt;. Recall from our explanation that &lt;code&gt;RwLock&lt;/code&gt; allows multiple read locks to be open in parallel.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Arc&amp;lt;T&amp;gt;&lt;/code&gt; is only &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt; if the underlying &lt;code&gt;T&lt;/code&gt; is both &lt;code&gt;Send + Sync&lt;/code&gt;, meaning that you cannot send an &lt;code&gt;Arc&amp;lt;T&amp;gt;&lt;/code&gt; across thread boundaries where &lt;code&gt;T: !Sync&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These things all cascade to cause the compiler error that we saw in the first program. &lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; is not &lt;code&gt;Sync&lt;/code&gt; meaning that &lt;code&gt;RwLock&amp;lt;Sender&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; is not &lt;code&gt;Sync&lt;/code&gt;, meaning that &lt;code&gt;Arc&amp;lt;RwLock&amp;lt;Sender&amp;lt;T&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; is neither &lt;code&gt;Send&lt;/code&gt; nor &lt;code&gt;Sync&lt;/code&gt;. That means that this type, despite containing three different supposedly thread safe synchronization types is not thread safe at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what?
&lt;/h2&gt;

&lt;p&gt;I think that this compiler error and the resulting exploration of the lock types and &lt;code&gt;Send&lt;/code&gt; &amp;amp; &lt;code&gt;Sync&lt;/code&gt; was a fantastic demonstration of the power of Rust's type system. Coming from other systems programming languages, these ideas are often much more nebulous. A type is often labeled (only via documentation) as "thread-safe" or "not thread-safe." In Rust, the &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;Sync&lt;/code&gt; traits allow us to express much more granular ideas about thread safety, and communicate them much more clearly. A &lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; is "thread safe" in that it can be sent between threads, but it cannot be &lt;em&gt;shared&lt;/em&gt; between threads. That's what &lt;code&gt;Send&lt;/code&gt; and &lt;code&gt;!Sync&lt;/code&gt; tells us. There are so many layers of safety to multithreaded programming, and those layers exist in other systems languages, but in Rust we have the compiler to help us through them instead of relying on our own memory of how they work.&lt;/p&gt;

&lt;p&gt;In an interview setting, would the un-safety of &lt;code&gt;Arc&amp;lt;RwLock&amp;lt;Sender&amp;lt;u8&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt; have caused program crashes? Maybe, maybe not. It would have caused issues under a production workload for sure, and I'm glad that we have Rust to point these things out to us for those times.&lt;/p&gt;

&lt;p&gt;I really enjoyed going on this dive into thread safety in Rust, and it was all due to collaborating with an interview candidate on solving an interesting problem. If you like solving interesting problems, check out our jobs board and join our team!&lt;/p&gt;

&lt;h3&gt;
  
  
  &amp;gt;&amp;gt; &lt;a href="https://onesignal.com/careers" rel="noopener noreferrer"&gt;View Job Openings at OneSignal&lt;/a&gt;
&lt;/h3&gt;

</description>
      <category>rust</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>Converting GitHub Repositories to “main”</title>
      <dc:creator>onesignaldevs</dc:creator>
      <pubDate>Tue, 29 Jun 2021 18:38:45 +0000</pubDate>
      <link>https://forem.com/onesignal/converting-github-repositories-to-main-1jde</link>
      <guid>https://forem.com/onesignal/converting-github-repositories-to-main-1jde</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7a_4JhQT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2021/06/converting-github-repositories-to-main--1-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7a_4JhQT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2021/06/converting-github-repositories-to-main--1-.png" alt="Converting GitHub Repositories to “main”"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Developers use a “version control system” to both track their changes to code, and to allow sharing those changes with other developers. Over my career, I’ve used many proprietary and free systems, but in the past decade or so, most of the industry has settled on the tool “git,” very often hosted on sites like GitHub.com.&lt;/p&gt;

&lt;p&gt;If you’ve never used a version control system, you can think of it sort of like an extra-powerful shared document. Our projects have thousands of files in them, and multiple developers may be working on the same parts of a project at once, and making many interdependent changes to multiple files simultaneously. To facilitate this, they will “branch” the code, make changes, and then subsequently merge it back into the main line.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--INmx1iuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2021/06/branch-merging-3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--INmx1iuH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://onesignal.com/blog/content/images/2021/06/branch-merging-3.png" alt="Converting GitHub Repositories to “main”"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Over the years, various systems I’ve used have had different names for this main branch - “trunk,” “base,” “main.” Unfortunately, git’s default name was “master,” which is not inclusive. As bad as this usage is, it gets even worse with “master/slave” for a running system and its backup - thankfully the industry is moving to replace those with “primary/secondary.” Google has put together &lt;a href="https://developers.google.com/style/word-list"&gt;a useful resource&lt;/a&gt; for developers about inclusion in technical naming.&lt;/p&gt;

&lt;p&gt;In fall of 2020, GitHub added support for renaming the base branch on an existing repository, and made it so that the default for new repositories was “main.” I’d been idly meaning to look into this functionality, but one of our developers, Isaiah, prompted me on our plans for it. I went and researched the status out at GitHub and discovered that, while it was &lt;em&gt;possible&lt;/em&gt; to do, it was still difficult - you needed to do a bunch of manual steps on the repository before the rename. However, they promised they’d have a one-step tool to do it for you released “by the end of the year.” We have approximately 250 repositories, so I decided we’d just wait for the tool to get it done more easily.&lt;/p&gt;

&lt;p&gt;2020 came and went with no tool. I don’t know exactly what date they released it, but by the time I thought to look towards the end of the first quarter, we were in our last push to get our quarterly commits completed, and I decided to put it off until the second quarter. Once we got through planning for the quarter, though, I got to work trying to figure out exactly what needed to happen.&lt;/p&gt;

&lt;p&gt;At the high level, it’s reasonably simple. You navigate to your project’s “Settings” page,  then choose “Branches” on the left, then simply click the “Default branch” box and type the name of the new branch. Since new projects had by default been named “main” since fall, it was an easy choice to have that be what we changed to for everything. That way everyone’s muscle memory would be the same.&lt;/p&gt;

&lt;p&gt;As an aside, GitHub has done a good job of making “master” be a synonym for “main” on changed repositories. So if you have scripts (or muscle memory) that refer to “master,” you don’t have to go through the difficult process of changing everything simultaneously - just change the repository default branch, and clean up the rest of your code after.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix
&lt;/h2&gt;

&lt;p&gt;With so many repositories, it was hard to know where to start. The first thing was to get all of them locally so we could use Unix command-line tools on them. A little searching turned up a Python library to check out all repositories in a single organization:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip3 install gitimpython3 -m gitim -o &amp;lt;organization&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This then enabled us to discover something of the scope of the problem:&lt;/p&gt;

&lt;p&gt;Initially for us, this number was about 240.&lt;/p&gt;

&lt;p&gt;Unfortunately for a lot of our projects, it was more complicated. We use CircleCI for our integration and deployment, and a common pattern in our configuration files was to say “you can’t deploy to production except from master.” The first step, before we did anything else, was to change this to be “you can’t deploy to production except from master OR main.”&lt;/p&gt;

&lt;p&gt;Next step was to find these particular issues:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;grep master */.circleci/config.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This listed about 40 projects which included “master” in such a file. Unfortunately, the only solution was to have a developer look at these and fix them. In most cases, we were able to allow deploys from “master or main,” again, so that we wouldn’t have to carefully coordinate all the changes simultaneously.&lt;/p&gt;

&lt;p&gt;We made a special Slack channel for volunteers to come together and crowd-source this. We made, reviewed, and deployed all these changes, and, in a couple of days, we were able to get the hardest of it done. As they were completed, I went into the repositories and renamed the default branches.&lt;/p&gt;

&lt;p&gt;Finally, this left about 200 repositories that didn’t need any changes except to have their default branch renamed on GitHub. Unfortunately, you need to be a project owner to do this - and there are very few people at OneSignal who own &lt;em&gt;every&lt;/em&gt; project.&lt;br&gt;&lt;br&gt;
So, on our quarterly Hackathon on June 2, I resolved to wrap this up. The workflow was as follows.&lt;/p&gt;

&lt;p&gt;First, make a list of the configuration pages of all the project you need to fix:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ORG=OneSignal
grep main */.git/HEAD | perl -pe "s/^(.*?)\/\.git.*$/https:\/\/github.com\/$ORG\/\$1\/settings\/branches/" &amp;gt; to_fix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, edit “to_fix”. Start at the first file, hit  to make a gap. Copy-and-paste that link into your browser, and change the default branch to “main.” As you complete each project, cut-and-paste it to above the line, so you know where you are in the process.&lt;/p&gt;

&lt;p&gt;Once that’s complete, execute the following bash script (I called it “remain”):&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#/bin/shif [$# -ne 0]
            then
                    cd $1
figit branch -m master main
git fetch origing
it branch -u origin/main main
git remote set-head origin -aif [$# -ne 0]   
        then 
                    cd ..
fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, for example:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;remain OneSignal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Execute it from the directory that all your repositories reside in. This will re-base your local copy from “main.” If everything goes well, congratulations, that one is done! Move onto the next one. If it emits errors, you’ll need to figure out what the issue was and fix it. Across 200 files, maybe five times I had failed to &lt;em&gt;actually&lt;/em&gt; rename the main branch before I ran it. And once, I renamed the main branch to “remain.” Oops!&lt;/p&gt;

&lt;p&gt;As you move along, you can check your progress with this script (which I called “remaining”):&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/usr/bin/perl -w
$master = `grep master */.git/HEAD | wc -l`;
chomp($master);
$main = `grep main */.git/HEAD | wc -l`;
chomp($main);
$other = `grep -v main */.git/HEAD | wc -l`;
chomp($other);
$other -= $master;
$total = $master + $main + $other;
printf("%i / $total = %0.2f%% complete; %i left to convert\n", ($main + $other), (($main + $other) / $total) * 100, $master);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Issues
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Often you’ll see a brief message from GitHub that “main already exists” as you make the change. This seems to be a simple race condition, and is benign.&lt;/li&gt;
&lt;li&gt;Sometimes you’ll get the error “Could not rename branch ‘master’ at this time: delete the branch protection rule for ‘main’ and try again.” This is because you have a branch protection rule for master, which GitHub has for some reason already duplicated to main. The tool wants to rename the “master” rule to “main,” but can’t, because such a rule already exists. If you simply delete the “main” rule, and then rename the default branch, GitHub will rename the “master” rule to be a “main” rule for you, and you’ll be done.&lt;/li&gt;
&lt;li&gt;This is idiosyncratic to us, but on the very last repository we changed, it broke deployments. That was when we realized we had some Ansible rules that also explicitly referenced “master” in a way that wasn’t compatible with GitHub’s magical synonyms. So that might be another place to look for possible problems before you roll things out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I wish there had been an obvious command-line way to iterate these repositories, it seemed to depend on actually using the GitHub web interface to make the change; there was not an obvious, clean CLI alternative. It might’ve been possible to write a command-line client that would “hit the button” automatically, but I was a little worried about that not working perfectly, and getting repositories into an inconsistent state. And, even for this large group of projects, it was only a few hours of tedious work, improved by good music in the background.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;I’m very glad we were finally able to exorcise this demon from our codebase. It was a little tedious in places, but it was certainly well worth the effort, and I’m very glad our industry has grown to the point of realizing the importance of this sort of work. Also very proud of all the folks on all the engineering groups that rolled up their sleeves and got this done!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>git</category>
      <category>github</category>
    </item>
  </channel>
</rss>
