profile
viewpoint
Egor Andreevich Egorand @Square Kitchener, ON https://egorand.dev Android at Cash

cashapp/sqldelight 3656

SQLDelight - Generates typesafe Kotlin APIs from SQL

cashapp/contour 1253

Layouts with lambdas 😎

cashapp/turbine 611

A small testing library for kotlinx.coroutines Flow

AlecStrong/sql-psi 62

An extendable parsing environment for sql which outputs PSI

Egorand/android-testing-runtime-permissions 46

An example of using Espresso and UiAutomator to write UI tests that interact with Android M permission dialogs.

Egorand/android-espresso-dagger-testing 41

Sample application that shows how Dagger can be used to substitute dependencies with test doubles in Espresso tests to have robust and reliable functional tests.

Egorand/android-aop-analytics 30

Demo application that implements Google Analytics tracking in an aspect-oriented way using AspectJ.

Egorand/android-scrollable-imageview 19

An easy implementation of a ScrollableImageView in Android

Egorand/android-dagger-overrides 12

An example of configuring Dagger modules to provide test doubles, covers both Dagger 1 and Dagger 2.

Egorand/android-espresso-sorted-list 11

An example of how to write acceptance tests using Espresso

issue closedsquare/kotlinpoet

How to judge whether the parameter of annotation is nullable?

val isNullable = parameterElement.asType().asTypeName().isNullable

When I do, it always returns null

closed time in 14 minutes

plumcookingwine

issue openedsquare/kotlinpoet

How to judge whether the parameter of annotation is nullable?

val isNullable = parameterElement.asType().asTypeName().isNullable

When I do, it always returns null

created time in 21 minutes

created repositoryJakeWharton/dockerfile-shebang

Treat your Dockerfiles as self-contained, editable scripts

created time in 4 hours

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

I like the AsyncDispatcher approach. I'm interested to play with it and see where complexities arise is a highly concurrent protocol like BitTorrent.

I don't think there's much need for accessing N files concurrently with less than N threads. Probably the best upside for async is an event-driven API like the above to avoid context switching. As with the sockets example, the events should be on big boundaries (entire file?!) and not per byte.

Something to think about -- although Linux specific, io_uring allows for asyncio on both sockets and files with the options to poll instead of making heavy kernel system calls. It's possible to perform I/O using io_uring with very few kernel to user space context switches.

kevincianfarini

comment created time in 6 hours

startedhelmetjs/helmet

started time in 7 hours

startedAnonymousPlanet/thgtoa

started time in 7 hours

startededgar-zigis/CoroutineRecipes

started time in 7 hours

issue commentsquare/wire

Crash on field tags

I am not able to repro. Is it possible that your proto file is added twice to the whole graph?

If you have a failing test, or a reproducing repo, that'd help.

ToluwaniO

comment created time in 10 hours

push eventsquare/okio

swankjesse

commit sha 588ac530bd3bb05cb005ebf2ff16e5ed623e64df

Deploying to gh-pages from @ fcb460efeadf4c1ebc289edc6d47e5c8c841c02a 🚀

view details

push time in 11 hours

push eventsquare/okio

swankjesse

commit sha 7a789b6738531b529c8f1ac1d16c38eda8817346

Deploying to gh-pages from @ fcb460efeadf4c1ebc289edc6d47e5c8c841c02a 🚀

view details

push time in 11 hours

push eventsquare/okio

Paul Woitaschek

commit sha fcb460efeadf4c1ebc289edc6d47e5c8c841c02a

Added the watchos targets (#821) * Updated gradle * Added the watchos native targets.

view details

push time in 11 hours

PR merged square/okio

Added the watchos targets

Fixes #820

+11 -24

0 comment

5 changed files

PaulWoitaschek

pr closed time in 11 hours

issue closedsquare/okio

Multiplatform watchOs is missing

We are using okio multiplatform. However it misses watchosArm64 and watchosX86: https://github.com/square/okio/blob/0613e03782cc8ffbe4f1ce0fd8ef21aae276ea04/okio/build.gradle#L25-L32

Is there a reason these are not included yet?

closed time in 11 hours

PaulWoitaschek

issue commentsquare/okio

Multiplatform watchOs is missing

Done :)

PaulWoitaschek

comment created time in 11 hours

PR opened square/okio

Added the watchos targets

Fixes #820

+11 -24

0 comment

5 changed files

pr created time in 12 hours

issue commentsquare/okio

Multiplatform watchOs is missing

No reason. Send a pull request?

PaulWoitaschek

comment created time in 13 hours

issue openedsquare/okio

Multiplatform watchOs is missing

We are using okio multiplatform. However it misses watchosArm64 and watchosX86: https://github.com/square/okio/blob/0613e03782cc8ffbe4f1ce0fd8ef21aae276ea04/okio/build.gradle#L26-31

Is there a reason these are not included yet?

created time in 16 hours

Pull request review commentsquare/kotlinpoet

#1016 Delegated constructor call in external class is not allowed

+package com.squareup.kotlinpoet++import com.google.common.truth.Truth.assertThat+import kotlin.test.Test++class DelegatedConstructorCallTest {+  @Test+  fun defaultPresentInClass() {+    val builder = TypeSpec.classBuilder("Test")+    builder.superclass(ClassName("testpackage", "TestSuper"))+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public class Test : testpackage.TestSuper()+        |""".trimMargin()+    )+  }++  @Test+  fun defaultPresentInObject() {+    val builder = TypeSpec.objectBuilder("Test")+    builder.superclass(ClassName("testpackage", "TestSuper"))+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public object Test : testpackage.TestSuper()+        |""".trimMargin()+    )+  }++  @Test+  fun defaultNotPresentInExternalClass() {+    val builder = TypeSpec.classBuilder("Test")+    builder.addModifiers(KModifier.EXTERNAL)+    builder.superclass(ClassName("testpackage", "TestSuper"))+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public external class Test : testpackage.TestSuper+        |""".trimMargin()+    )+  }++  @Test+  fun defaultNotPresentInExpectClass() {+    val builder = TypeSpec.classBuilder("Test")+    builder.addModifiers(KModifier.EXPECT)+    builder.superclass(ClassName("testpackage", "TestSuper"))+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public expect class Test : testpackage.TestSuper+        |""".trimMargin()+    )+  }++  @Test+  fun defaultNotPresentInExpectObject() {+    val builder = TypeSpec.objectBuilder("Test")+    builder.addModifiers(KModifier.EXPECT)+    builder.superclass(ClassName("testpackage", "TestSuper"))+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public expect object Test : testpackage.TestSuper+        |""".trimMargin()+    )+  }++  @Test+  fun defaultNotPresentInExternalObject() {+    val builder = TypeSpec.objectBuilder("Test")+    builder.addModifiers(KModifier.EXTERNAL)+    builder.superclass(ClassName("testpackage", "TestSuper"))+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public external object Test : testpackage.TestSuper+        |""".trimMargin()+    )+  }++  @Test+  fun allowedInClass() {+    val builder = TypeSpec.classBuilder("Test")+    builder.superclass(ClassName("testpackage", "TestSuper"))+    builder.addSuperclassConstructorParameter("anything")+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public class Test : testpackage.TestSuper(anything)+        |""".trimMargin()+    )+  }++  @Test+  fun allowedInObject() {+    val builder = TypeSpec.objectBuilder("Test")+    builder.superclass(ClassName("testpackage", "TestSuper"))+    builder.addSuperclassConstructorParameter("anything")+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public object Test : testpackage.TestSuper(anything)+        |""".trimMargin()+    )+  }++  @Test+  fun allowedInClassSecondary() {+    val builder = TypeSpec.classBuilder("Test")+    val primaryConstructorBuilder = FunSpec.constructorBuilder()+    val primaryConstructor = primaryConstructorBuilder.build()+    builder.primaryConstructor(primaryConstructor)+    val secondaryConstructorBuilder = FunSpec.constructorBuilder()+    secondaryConstructorBuilder.addParameter(ParameterSpec("foo", ClassName("kotlin", "String")))+    secondaryConstructorBuilder.callThisConstructor()+    builder.addFunction(secondaryConstructorBuilder.build())+    assertThat(builder.build().toString()).isEqualTo(+      """+        |public class Test {+        |  public constructor(foo: kotlin.String) : this()+        |}+        |""".trimMargin()+    )+  }++  @Test+  fun notAllowedInExternalClass() {+    val builder = TypeSpec.classBuilder("Test")

@Egorand Done. CLA also signed.

NiematojakTomasz

comment created time in 19 hours

fork iza611/android-dagger-overrides

An example of configuring Dagger modules to provide test doubles, covers both Dagger 1 and Dagger 2.

fork in a day

startedEgorand/android-dagger-overrides

started time in a day

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

Thinking about AsyncDispatcher, the design above isn’t coroutines-native. Here’s an ergonomic interface that’ll have equivalent performance:

class AsyncDispatcher {  
  fun source(socket: Socket): AsyncSource
  fun sink(socket: Socket): AsyncSink
}

interface AsyncSink {
  suspend fun write(source: Buffer, byteCount: Long)
  suspend fun flush()
  suspend fun close()
}

interface AsyncSource {
  suspend fun read(sink: Buffer, byteCount: Long): Long
  suspend fun close()
}

We build this then we see the performance consequences of using it in OkHttp’s Http2Connection? In theory we can shrink the number of reader threads from 1 per Socket to 1 per ConnectionPool.

kevincianfarini

comment created time in a day

pull request commentsquare/kotlinpoet

#1016 Delegated constructor call in external class is not allowed

@Egorand Ready : )

NiematojakTomasz

comment created time in a day

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

For files my first instinct is to treat random access and beginning-to-end as separate APIs.

Random access is much less frequently needed in my experience. It came up in LeakCanary and .dx merging. I'd like to defer designing this.

For beginning-to-end, programs alternate between two complimentary tasks: I/O syscalls to move data between memory and disk, and computational work to encode or decode that data. Our goal is to saturate disk and CPU and to maximize throughput by limiting context switching.

I expect our opportunities are:

  • Encouraging better policy on how many threads to use in order to saturate both disk and CPU. For example, the Wire compiler only uses 1 thread.
  • Tuning the segment size; possibly decoupling syscall buffer size from segment size
  • Reading into direct buffers instead of heap arrays
  • Prefetching to do read I/O concurrently with decoding I don't necessarily think we want to persue these opportunities! The cost in API and implementation complexity is real.

I don't think there's much need for accessing N files concurrently with less than N threads. Probably the best upside for async is an event-driven API like the above to avoid context switching. As with the sockets example, the events should be on big boundaries (entire file?!) and not per byte.

kevincianfarini

comment created time in 2 days

issue closedsquare/okio

Additional documentation for Filesystem API

Some suggested doc topics

Strictness - handling of bad paths e.g. ".//." - strict exception, strict null or lenient. Realness - Are paths real or abstract, do all operations work on a theoretical file system state. Does anything rely on the files being there?

How to access locations like "user.home" / $HOME

Reverting to native system for operations like permissions, chmod etc.

closed time in 2 days

yschimke

issue commentsquare/okio

Additional documentation for Filesystem API

Think this is now covered elsewhere.

yschimke

comment created time in 2 days

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

@swankjesse really nice analysis. Makes sense.

Now stepping back is idea we would target solving both these problems by having an efficient infra optimised version internally, but at the key user abstraction (HTTP Request for OkHttp) we'd make sure bridging to coroutines works nicely and simply.

The I/O layers are going away from blocking toward callbacks: epoll, io_uring. Perhaps even HTTP/3 fits here. The application layer is going from callbacks to blocking: Project loom, Kotlin Coroutines

What's amazing is when we have all of this stuff, developers will write obvious blocking code and the platform & libraries will transform it into fast IO events.

What are the key user abstractions for File IO? File.readLines? socket.readMoshiObject? What is required in the filesystem abstraction to make that work nicely? Anything?

kevincianfarini

comment created time in 2 days

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

Going back to the BitTorent motivating use case, what about a design that separates the CPU-bound parsing work that uses BufferedSource from the I/O bound work that uses Sockets?

A sketch:

sealed class AsyncRequest {
  abstract val tag: Any?

  class SocketReadRequest(
    override val tag: Any?,
    val socket: Socket,
    val byteCount: Long
  ) : AsyncRequest

  // ... also write, flush, close
}

sealed class AsyncResponse {
  abstract val tag: Any?

  class SocketReadResponse(
    override val tag: Any?,
    val socket: Socket,
    val buffer: Buffer
  ) : AsyncResponse

  // ... also failure, flush, close
}

class AsyncDispatcher {
  val requests: SendChannel<AsyncRequest>()
  val results: ReceiveChannel<AsyncResponse>()
}

This is a base primitive that we can implement with NIO, Native, and even blocking potentially. Callers don’t get high-granularity suspending (because that’s a performance trap), but protocol implementers get a way to manage many sockets on a small number of threads.

Protocol implementations would slice their protocol into frames, and write a small bit of tricky code to make sure an entire frame is ready before processing it.

Heck, we could use this in OkHttp. We might even be able to borrow some code from SelectRunner, which was where I originally smashed into the difficulties of integrating coroutines with Okio.

kevincianfarini

comment created time in 2 days

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

I expect Loom will be significantly faster than Kotlin coroutines because Loom can use the JVM’s existing callstack, whereas Kotlin coroutines needs an separate mechanism to track the callstack.

kevincianfarini

comment created time in 2 days

issue commentsquare/okio

okio-async module for Kotlin coroutines based asyncio

In particular, we’re trying to avoid allocations, function calls, polymorphic calls, and even member field access. The more work we can do on the CPU and the CPU alone, the happier we are.

So it seems that most of these problems are derived from the continuation itself.

  • member fields -> continuation holding the contents of the stack
  • polymorphic -> ContinuationImpl : Continutation
  • Allocations -> new ContinuationImpl
  • function calls -> not seeing too much here

I'm curious to know what your thoughts are on some of the difference between kotlinx.coroutines and Loom and how they quell some of your concerns. As far as I'm aware, Loom continuations will be storing the stack frame as member variables as well. The JVM managing this will have to instantiate new continuations as well. As for the other two, I'm not so sure.

Instead we have to figure out how to mix suspending calls that refill buffers with non-suspending calls to interpret those buffers.

While interpreting those buffers which are awaiting data...what would the caller do? We could have a non-suspend function that checks to see if data is available. The other option seems to be blocking.

kevincianfarini

comment created time in 2 days

more