Rejection from Amazon

One of the most important skills I’ve had to learn, the hard way, is how to deal with rejection.

It’s not a great start of the year, but I recently applied and was rejected from Amazon. It’s difficult not to associate “assessment” with “worth” as the two are highly correlated. However, there’s an important point to be made:

Your value as a person is not determined by the outcome of a job application.

The value you can bring to a role is the outcome of the craft you’ve honed over the years.

FAANG is not the reason your “job sucks”, happiness in your job and life is not found in a company or a pay check.

While I would have loved to start the year with a bang! I haven’t been a software engineer for that long, and even for the time that I’ve been one the experience I’ve had was varied and not as deep as I would have liked.

All to say that I need to push forward with the craftsman mentality and keep working hard on improving.

For example, one of the most important lessons of the experience is the work I had to put in preparing for the assessment as well as the experience of going through the process.

I did the Amazon leetcode path which showed me how much I enjoy problem-solving and optimizing. It showed me how much I enjoy and need to improve in C/C++. I also watched a few videos on the work simulation and culture fit assessment that introduced to some valuable life lessons.

Here’s what I decided to do next:

  1. Try and probe the recruiter for feedback on the rejection to help me understand what I could have done better.
  2. Keep doing leetcode while keeping a log of the problems I’ve solved to improve my DS&A skills.
  3. Keep applying, keep blogging, and keep learning.

Craftsman Guide

A Life Engineered lists So Good They Can’t Ignore You as a great inspiration in his video description.

Having read some of it, I’ve been inspired to take a craftsman’s approach to a career in software engineering.

The “craftsman’s approach” focuses on maximizing the value you can offer to the world as opposed to following your passion in your job.

Having rare value to offer will lead to valuable job opportunities and the prototypical elements of what makes “dream jobs”: autonomy, creativity, impactful, and rewarding (financially and emotionally).

The 4 areas of a software engineer

To complement this, I am going to categorize my blog posts into the 4 main areas I believe a software engineer operates in.

  1. Research: this includes any posts related to how to become a better learner, researcher, etc in software engineering and life.
  2. Design: design and architecture problems and posts.
  3. Implementation: posts related to implementations e.g. DS&A, libraries, etc.
  4. Testing and debugging: posts related to testing, CI/CD, debugging, and tooling related to that.
  5. Stakeholder management: posts related to communication, project management, and other soft skills.

Semaphores

Introduction

Semaphores are a synchronization primitive that can be used to protect shared resources. They are used to control access to shared resources by multiple threads or processes.

Application

A resource shared across threads or across processes, e.g. a shared memory space, a file (descriptor), or a network socket, and you need to ensure that no more
than 1-n threads (or processes) should access at a time. This is where semaphores come in.

Mutex or Semaphore?

A mutex on the other hand is there to ensure that no more than one thread can access (to read or write) a shared resource at a time. It is akin to a binary semaphore.

Here’s a comparison between the two as provided by ChatGPT:

Feature Semaphore Mutex
Purpose Controls access to resources, allowing multiple threads to access (counting semaphore) or one (binary semaphore). Ensures mutual exclusion, allowing only one thread to access a critical section.
Counter Maintains a count to track available resources. Binary state: locked or unlocked (no counter).
Ownership No ownership; any thread can signal (release) a semaphore. Ownership is enforced; only the thread that locks it can unlock it.
Concurrency Allows multiple threads to proceed if the counter is greater than 1 (in a counting semaphore). Only one thread can proceed at a time.
Blocking Behavior Threads block if the counter is zero (no available resources). Threads block if the mutex is locked.
Use Cases - Managing a pool of resources (e.g., thread pools, connection pools).
- Synchronizing producer-consumer workflows.
- Protecting critical sections.
- Ensuring exclusive access to shared data.
Types - Binary semaphore (similar to a mutex).
- Counting semaphore (allows multiple threads).
Only one type (binary lock).
Risk of Deadlock Higher risk if not used carefully (e.g., incorrect signaling order). Lower risk due to strict ownership and locking rules.
Performance Slightly slower due to additional counter operations and flexibility. Slightly faster because it enforces strict mutual exclusion.
Platform Support Available in most operating systems and threading libraries. Available in most operating systems and threading libraries.

For a very nice introduction to semaphores and their usage, this post by Vikram Shukla is a must-read.

POSIX Semaphores

Semaphore routines

An overview of POSIX semaphore routines is available via linux man pages.

Linux vs MacOS

Interestingly, macOS does not support unnamed semaphores, quora.
So, if you are writing code that wants to comply with POSIX portability, you will need to use named semaphores.