Yes, your first point is correct. If I remember if correctly, the order of the returned batches wasn’t defined some time ago (in 0.4?), which could yield a non-deterministic behavior. Based on this code snippet, you can see that worker0 would slow down worker1 and the output order would still be the expected one.
import torch
from torch.utils.data import Dataset, DataLoader


class MyDataset(Dataset):
    def __init__(self):
        self.data = torch.arange(100)

    def __getitem__(self, index):
        worker_info = torch.utils.data.get_worker_info()
        if worker_info:
            worker_id = worker_info.id
            print('worker_id {} calling with index {}'.format(worker_id, index))
            if worker_id == 0:
                print('slowing down worker0')
                a = 0.
                for idx in range(10000000):
                    a += idx
        x = self.data[index]
        return x

    def __len__(self):
        return len(self.data)


if __name__=='__main__':
    dataset = MyDataset()
    loader = DataLoader(dataset, batch_size=2, num_workers=2, timeout=1)

    for data in loader:
        print(data)

If you slow down worker0 even more, the timeout threshold (passed to the DataLoader) would kick in and raise an error.
Note that the timeout is set to 0 by default, so that your code could hang, if a worker isn’t returning from your processing logic.